TCPServer in Godot – Complete Guide

Welcome game developers, whether you’re new to the scene or a seasoned coder looking to enhance your skillset! Today, we’ve got something pretty exciting to dive into—the world of networking in games using Godot 4. Specifically, we’ll be exploring the TCPServer class. This nifty class enables your game to listen for and handle incoming TCP connections, laying the groundwork for multiplayer experiences, chat systems, and more. So grab your virtual gear as we delve into the ins and outs of TCPServer, your trusty sidekick on the journey to networked game creation.

What is TCPServer?

TCPServer in Godot is a class that acts similarly to a welcoming host in the online multiplayer world. It sits patiently, listening for any incoming connections from players around the globe. Once a connection is detected, it creates a channel where data can flow back and forth, much like a private conversation at a bustling party.

What is it for?

With TCPServer, you can architect the backbone of a networked game. It can serve as the entry point for players to join a game server, for clients to fetch updates from a central repository, or even to build chat services within the game. The possibilities it unlocks for your game’s connectivity are vast and impactful.

Why should I learn it?

Understanding and utilizing TCPServer is a stepping stone to making games that people can play together, no matter where they are. It’s an essential skill for creating that sense of community and competition that comes with multiplayer features. By learning how to implement TCPServer, you open up a whole new dimension of game development, and we’re here to guide you through that process. Let’s get started and see what TCPServer can do for your project!

CTA Small Image
FREE COURSES AT ZENVA
LEARN GAME DEVELOPMENT, PYTHON AND MORE
ACCESS FOR FREE
AVAILABLE FOR A LIMITED TIME ONLY

Initializing a TCPServer

Before we dive into accepting connections, we first need to set up and initialize our TCPServer. Here’s how to create an instance of TCPServer and how to start listening for incoming connections on a particular port.

var server = TCPServer.new()

func _ready():
    if server.listen(8080) == OK:
        print("Server is listening on port 8080")
    else:
        print("Failed to start server")

This snippet creates a new TCPServer instance and starts it on port 8080. Always check for errors to ensure the server starts successfully.

Accepting Connections

Once your server is setup, the next step is to accept incoming connections. Below is a simple example of how to accept a connection on the server:

func _process(delta):
    if server.is_connection_available():
        var client = server.take_connection()
        print("New client connected: ", client.get_peer_address())

In this code, we continuously check if a new connection is available. When a client connects, the server accepts the connection and prints the client’s address.

Communication between Server and Client

After a connection is established, you’ll want to send and receive data. This example shows how the server can send a welcome message to the client.

func _process(delta):
    if server.is_connection_available():
        var client = server.take_connection()
        var welcome_message = "Welcome to the server!"
        client.put_data(welcome_message.to_utf8())

Similarly, here’s how you can receive data sent by the client:

func _process(delta):
    var client = server.take_connection()
    if client.get_available_bytes() > 0:
        var data = client.get_data(1024) # Read up to 1024 bytes
        print("Received from client: ", data.get_string_from_utf8())

These examples demonstrate simple one-way communication, sending and receiving strings converted from and to UTF-8.

Handling Multiple Clients

In a real-world scenario, you’ll need to handle multiple concurrent connections. That’s where the select method comes into play, checking which clients are ready for interaction.

var peer_pool = []

func _process(delta):
    if server.is_connection_available():
        var client = server.take_connection()
        peer_pool.append(client)

    for i in range(len(peer_pool)):
        var peer = peer_pool[i]
        if peer.get_status() == StreamPeerTCP.STATUS_CONNECTED:
            if peer.get_available_bytes() > 0:
                var data = peer.get_data(1024)
                print("Received from client: ", data.get_string_from_utf8())
        else:
            peer_pool.remove(i)

In this example, we store connected clients in a `peer_pool`. We loop through it, read available data from each connected client, and remove any clients that have disconnected. This allows our server to manage several clients simultaneously.

In the next part, we will cover advanced topics such as handling disconnections and sending data to all connected clients, ensuring that your server is robust and interactive. Stay tuned!Great, let’s press forward and add more depth to our TCPServer capabilities. Game network programming can get quite complex, but Godot simplifies this process significantly. Ready to handle disconnections and broadcast messages? Let’s roll!

Handling Disconnections

A robust server must also manage the inevitable—clients will disconnect. Here’s how you can handle this scenario:

func _process(delta):
    for i in range(len(peer_pool)):
        var peer = peer_pool[i]
        # Check if the peer is still connected
        if peer.get_status() != StreamPeerTCP.STATUS_CONNECTED:
            peer_pool.remove(i)
            print("Client has disconnected")

Make it a routine to check the status of each peer. If a client has disconnected, remove them from the peer pool and take any other necessary actions, such as notifying other clients or saving the state of the game.

Sending Data to All Clients

Sending a message to all connected clients is a common requirement, especially when you need to broadcast game updates or announcements. Here’s a method to do just that:

func broadcast_message(message):
    var data = message.to_utf8()
    for peer in peer_pool:
        if peer.get_status() == StreamPeerTCP.STATUS_CONNECTED:
            peer.put_data(data)

This function converts a message to UTF-8 and loops through all connected peers, sending the data to each one. Simple, but incredibly effective for synchronizing game states across clients.

Asynchronous Communication

Godot is designed to be user-friendly, and that includes making asynchronous communication manageable. Here’s a snippet for setting up such a mechanism:

var thread = Thread.new()

func start_async_server():
    thread.start(self, "_thread_function")

func _thread_function():
    while true:
        # Your server logic here
        # Use `OS.delay_msec(10)` to prevent the thread from using 100% CPU
        OS.delay_msec(10)

This code starts a new thread to handle server tasks, allowing the main game loop to run unimpeded. Remember to include pauses to prevent the server thread from hogging CPU cycles.

Receiving and Processing Data Asynchronously

To process incoming data asynchronously, you’ll want to do something like this:

func _thread_function():
    while true:
        for peer in peer_pool:
            if peer.get_available_bytes() > 0:
                var data = peer.get_data(1024)
                process_data(data)
        OS.delay_msec(10)

func process_data(data):
    # Process data received from client

Here, the server checks each client for available data and processes it separately. This avoids blocking the main thread and allows for smoother gameplay and server management.

Implementing a Simple Chat Server

Let’s apply what we’ve learned to create a simple chat server:

func _process(delta):
    if server.is_connection_available():
        var client = server.take_connection()
        peer_pool.append(client)
        print("New client connected")

    for i in range(len(peer_pool)):
        var peer = peer_pool[i]
        if peer.get_status() == StreamPeerTCP.STATUS_CONNECTED:
            if peer.get_available_bytes() > 0:
                var data = peer.get_data(1024)
                var message = data.get_string_from_utf8()
                print("Client says: ", message)
                broadcast_message(message)
        else:
            peer_pool.remove(i)

This server accepts new connections, reads incoming messages, and broadcasts them to all other clients. It is just the foundation—real-world applications would require more robust input validation and error handling.

Implementing a full-fledged chat server goes beyond the scope of this article, but these basics set you up for that next step. Remember, the key to great multiplayer experiences lies in sturdy networking—and with TCPServer in Godot, you’re well on your way to creating engaging, connected games.

Keep coding and happy developing! Don’t forget that at Zenva, we’re committed to providing high-quality content that helps you learn and excel in your coding and game creation journey.Continuing our deep dive into networking with Godot, let’s further enrich our chat server with features such as command handling, private messages, and user identification. Get ready for another round of code snippets and explanations that will elevate your game’s networking capabilities!

Command Handling

A chat server often includes commands for users to execute specific actions. Consider a command that allows users to change their nickname:

func process_data(data, peer_id):
    var message = data.get_string_from_utf8()
    if message.begins_with("/nick"):
        var new_nick = message.split(" ")[1]
        change_nickname(peer_id, new_nick)
        broadcast_message("User " + str(peer_id) + " is now known as " + new_nick)
    else:
        broadcast_message(message)

func change_nickname(peer_id, new_nick):
    # code to change the user's nickname in your tracking system

This function looks for the `/nick` command followed by the desired nickname and broadcasts the change to all users.

Private Messaging

To allow users to send messages privately to each other, we can implement a direct message feature:

func process_data(data, peer_id):
    var message = data.get_string_from_utf8()
    if message.begins_with("/pm"):
        var parts = message.split(" ")
        var target_id = parts[1].to_int()
        var private_message = parts.slice(2, parts.size()).join(" ")
        send_private_message(peer_id, target_id, private_message)

func send_private_message(from_id, to_id, private_message):
    var target_peer = find_peer_by_id(to_id)
    if target_peer:
        var dm = "Private message from " + str(from_id) + ": " + private_message
        target_peer.put_data(dm.to_utf8())

This snippet allows users to send a private message using the `/pm` command followed by the target user’s ID and the message.

User Identification

Keeping track of users by their peer connection alone is not ideal. You’ll want to assign a unique identifier or use a chosen username:

var user_id_counter = 1
var user_db = {}

func _process(delta):
    if server.is_connection_available():
        var client = server.take_connection()
        var user_id = user_id_counter
        user_id_counter += 1
        user_db[user_id] = client
        print("User " + str(user_id) + " connected")

With each new connection, we assign a unique user ID and store it alongside the client in a dictionary for easy reference.

Handling Malicious Input

When building a networked application, always consider the security aspect. Here’s a simple method to sanitize incoming messages:

func sanitize_message(raw_data):
    var message = raw_data.get_string_from_utf8()
    # Simple sanitation example - replacing problematic characters
    message = message.replace(";", ":").replace("&", "and")
    return message

func process_data(data, peer_id):
    var message = sanitize_message(data)
    broadcast_message("User " + str(peer_id) + " says: " + message)

This function ensures that certain characters or strings that could be used maliciously are replaced or removed. Implementing robust sanitation is a crucial part of creating a secure networked game or service.

Graceful Server Shutdown

Finally, it’s essential to handle the shutting down of your server gracefully. When the server closes, you’ll want to notify connected clients and shut down cleanly:

func _exit_tree():
    broadcast_message("Server is shutting down")
    for peer in peer_pool:
        peer.disconnect_client()
    server.stop()

This function is called when the server node is about to be removed from the scene tree. It informs clients about the shutdown and cleanly disconnects them.

These codes snippets should guide you towards implementing more complex features in your networked Godot games. Remember, as you dive into the exciting world of game development, Zenva is here with curated courses and tutorials to keep you ahead of the learning curve. See you in the next lesson, and keep crafting those amazing multiplayer experiences!

Continue Your Game Development Journey

Delving into the networking functionality of Godot 4 is just the beginning of your journey into game development. Whether you’ve just started this path or looking to expand your existing expertise, continuous learning is the key to honing your skills and keeping up with the latest advancements in the industry.

To further your education, we at Zenva highly recommend checking out our Godot Game Development Mini-Degree. It’s a thorough and comprehensive program that can transform you from a game development enthusiast into someone capable of creating professional, cross-platform games using the impressive capabilities of Godot 4. You’ll get your hands dirty with practical projects across a variety of game genres, mastering elements such as 2D and 3D gameplay, UI systems, and interactive game mechanics.

For an even broader range of topics, explore our full collection of Godot courses. Each course is designed to boost your career, offering you the chance to earn certificates and build a project portfolio. With Zenva, you can start as a beginner and progress to a professional developer. So don’t wait—dive into our resources and keep building the game development career of your dreams!

Conclusion

Embarking on the journey of mastering networked game development with Godot 4 is a challenging yet incredibly rewarding endeavor. With each line of code, you not only enhance your technical skills but also unlock endless opportunities to connect players worldwide through your creations. TCPServer is just one piece of the vast Godot ecosystem, but it’s instrumental in weaving the social tapestry that makes multiplayer games so engaging. As you continue to experiment, innovate, and learn, remember that the world of game development is constantly evolving, and staying ahead means never stopping your educational quest.

We at Zenva are committed to supporting you every step of the way. Whether you’re designing the next hit multiplayer game or adding networked features to a passion project, Zenva’s Godot Game Development Mini-Degree is your portal to acquiring the skills you need to succeed. Join us, become part of a vibrant community, and let’s create incredible gaming experiences together. Your game development future is bright, and it starts right here, right now. Let’s code it into reality!

FREE COURSES
Python Blog Image

FINAL DAYS: Unlock coding courses in Unity, Godot, Unreal, Python and more.