Telepathy Networking Library

repository·master·Indexed 22 days ago

https://github.com/mirrornetworking/telepathy

A simple, message-based, allocation-free TCP networking library written in C# for high-performance, MMO-scale networking in Unity and Mirror. It focuses on stability and low GC overhead, utilizing message framing to ensure data integrity and background threads for non-blocking connectivity. The library provides Client and Server classes with an event-driven API (OnConnected, OnData, OnDisconnected) and a Tick() method for processing messages on the main thread.

Tokens
4.3K
Snippets
6
Records
21
Agent score
78%

What's inside Telepathy

  1. Core Concepts of Telepathy

    master

    Telepathy is a message-based, allocation-free TCP networking library designed for high-performance MMO-scale applications.

    Key characteristics include:

    • Message Framing: Telepathy uses framing to ensure that messages sent are received exactly as they were sent. You will never receive partial messages or concatenated messages (e.g., sending A and B results in receiving A then B, never AB).
    • Concurrency: It utilizes two threads per connection to leverage multi-core processors.
    • Allocation Free: The library is designed to avoid allocations in the 'hot path' to prevent Garbage Collection (GC) spikes during gameplay.
    • Simplicity: It abstracts low-level socket complexity into a simple Connect/Send/Disconnect/Tick workflow.
  2. Run Telepathy Load Test clients

    master

    To spawn multiple clients for load testing, navigate to the bin/Debug directory and execute LoadTest.exe with the client argument. The command requires the target IP address, the port number, and the number of clients to spawn.

    Example: To run 1000 clients connected to localhost on port 1337:

    cd bin/Debug
    mono LoadTest.exe client 127.0.0.1 1337 1000
  3. Integrate Telepathy with Unity

    master

    When using Telepathy in Unity, follow these steps for proper integration:

    1. Enable Background Running: Set Application.runInBackground = true in Awake to ensure networking continues when the window loses focus.
    2. Configure Logging: Redirect Telepathy's internal logging to Unity's console by assigning Debug.Log, Debug.LogWarning, and Debug.LogError to Telepathy.Logger.
    3. Update Loop: Call client.Tick(limit) and server.Tick(limit) inside the Unity Update() method. Even if the client/server is not currently connected/active, calling Tick is necessary to process disconnection messages.
    4. Cleanup: Call client.Disconnect() and server.Stop() in OnApplicationQuit() to ensure threads are shut down correctly when exiting the Editor or the build.
    using System;
    using UnityEngine;
    
    public class SimpleExample : MonoBehaviour
    {
        Telepathy.Client client = new Telepathy.Client(1024);
        Telepathy.Server server = new Telepathy.Server(1024);
    
        void Awake()
        {
            // update even if window isn't focused, otherwise we don't receive.
            Application.runInBackground = true;
    
            // use Debug.Log functions for Telepathy so we can see it in the console
            Telepathy.Logger.Log = Debug.Log;
            Telepathy.Logger.LogWarning = Debug.LogWarning;
            Telepathy.Logger.LogError = Debug.LogError;
    
            // hook up events
            client.OnConnected = () => Debug.Log("Client Connected");
            client.OnData = (message) => Debug.Log("Client Data: " + BitConverter.ToString(message.Array, message.Offset, message.Count));
            client.OnDisconnected = () => Debug.Log("Client Disconnected");
    
            server.OnConnected = (connectionId) => Debug.Log(connectionId + " Connected");
            server.OnData = (connectionId, message) => Debug.Log(connectionId + " Data: " + BitConverter.ToString(message.Array, message.Offset, message.Count));
            server.OnDisconnected = (connectionId) => Debug.Log(connectionId + " Disconnected");
        }
    
        void Update()
        {
            // client
            if (client.Connected)
            {
                // send message on key press
                if (Input.GetKeyDown(KeyCode.Space))
                    client.Send(new ArraySegment<byte>(new byte[]{0x1}));
            }
    
            // tick to process messages
            // (even if not connected so we still process disconnect messages)
            client.Tick(100);
    
            // server
            if (server.Active)
            {
                if (Input.GetKeyDown(KeyCode.Space))
                    server.Send(0, new ArraySegment<byte>(new byte[]{0x2}));
    
            }
    
            // tick to process messages
            server.Tick(100);
        }
    
        void OnGUI()
        {
            // client
            GUI.enabled = !client.Connected;
            if (GUI.Button(new Rect(0, 0, 120, 20), "Connect Client"))
                client.Connect("localhost", 1337);
    
            GUI.enabled = client.Connected;
            if (GUI.Button(new Rect(130, 0, 120, 20), "Disconnect Client"))
                client.Disconnect();
    
            // server
            GUI.enabled = !server.Active;
            if (GUI.Button(new Rect(0, 25, 120, 20), "Start Server"))
                server.Start(1337);
    
            GUI.enabled = server.Active;
            if (GUI.Button(new Rect(130, 25, 120, 20), "Stop Server"))
                server.Stop();
    
            GUI.enabled = true;
        }
    
        void OnApplicationQuit()
        {
            client.Disconnect();
            server.Stop();
        }
    }
  4. Use the Telepathy Server

    master

    To implement a server, instantiate Telepathy.Server with a buffer size. You must hook up event handlers for OnConnected, OnData, and OnDisconnected.

    Important: The ArraySegment<byte> provided in the OnData event is only valid until the event handler returns. To avoid issues, do not store this segment for later use; copy the data if you need it beyond the scope of the callback.

    To process incoming messages, you must call Tick(limit) within your application's main update loop. The limit parameter helps prevent deadlocks.

    // create server & hook up events
    // note that the message ArraySegment<byte> is only valid until returning (allocation free)
    Telepathy.Server server = new Telepathy.Server(1024);
    server.OnConnected = (connectionId) => Console.WriteLine(connectionId + " Connected");
    server.OnData = (connectionId, message) => Console.WriteLine(connectionId + " Data: " + BitConverter.ToString(message.Array, message.Offset, message.Count));
    server.OnDisconnected = (connectionId) => Console.WriteLine(connectionId + " Disconnected");
    
    // start
    server.Start(1337);
    
    // tick to process incoming messages (do this in your update loop)
    // => limit parameter to avoid deadlocks!
    server.Tick(100);
    
    // send a message to client with connectionId = 0 (first one)
    byte[] message = new byte[]{0x42, 0x13, 0x37};
    server.Send(0, new ArraySegment<byte>(message));
    
    // stop the server when you don't need it anymore
    server.Stop();
  5. Initialize and manage a Telepathy Server

    master

    The Server class is the primary entry point for hosting a Telepathy server. It manages multiple client connections using a background listener thread and dedicated threads for sending and receiving data for each client.

    To use the server:

    1. Instantiate Server with a MaxMessageSize.
    2. Call Start(port) to begin listening for connections.
    3. Use Tick(processLimit) in your main loop to process incoming network events.
    4. Call Stop() to shut down the server and close all connections.
  6. Configure Client queue limits

    master

    You can tune the following properties on the Client instance to manage memory and latency:

    • SendQueueLimit: The maximum number of messages allowed in the outgoing queue. If this limit is exceeded, the client disconnects. Default is 10000.
    • ReceiveQueueLimit: The maximum number of messages allowed in the incoming queue. Default is 10000.
  7. Configure Server queue limits

    master

    You can tune the memory usage and reliability of the server by adjusting the queue limits before calling Start():

    • SendQueueLimit: The maximum number of messages allowed in a client's outgoing queue. If exceeded, the client is disconnected. Default is 10000.
    • ReceiveQueueLimit: The maximum number of messages allowed in the shared receive pipe. Default is 10000.
  8. Telepathy Load Test performance benchmarks

    master

    The load test simulates clients sending 100 bytes 14 times per second, with the server echoing the message back.

    Benchmarks on a 2.2 GHz Intel Core i7 (Macbook Pro) for 1000 clients:

    ClientsCPU UsageRam UsageBandwidth Client+ServerResult
    1287%26 MB1-2 MB/sPassed
    50028%51 MB3-4 MB/sPassed
    100042%75 MB3-5 MB/sPassed