Skip to content

Repository files navigation

MineChat Protocol (.NET)

C# implementation of the MineChat wire protocol -- a secure, binary-framed, compressed protocol for chatting with Minecraft servers without being logged into the game.

Features

  • Connect & chat -- MineChatClient handles linking, authentication, keep-alive, and bidirectional chat with event-driven message/moderation dispatch
  • Build your own server -- MineChatConnection wraps any Stream (TCP, TLS, in-memory) into a packet-level read/write interface
  • Packet construction -- Typed payload records for all 9 packet types, with CBOR serialization/deserialization
  • Text components -- Parse, create, and render Minecraft JSON text components (colors, click/hover events, formatting inheritance)

Spec compliance

  • All 9 packet types (LINK, LINK_OK, CAPABILITIES, AUTH_OK, etc.) with typed payloads
  • Implements CBOR serialization with keyed maps per section 6 (using System.Formats.Cbor)
  • zstd compression using ZstdNet
  • Fully implements binary framing, with 1 MiB size limit enforcement
  • TLS + certificate pinning -- MineChatClient handles TLS connections with trust-on-first-use pinning
  • Keep-alive -- PING/PONG with RTT tracking
  • Moderation support -- Handles warn, mute, kick, ban actions at client and account scope

Getting started

dotnet add package MineChat.Protocol

Connecting as a client

The simplest path -- use the bundled MineChatClient:

using MineChat.Protocol.Networking;

var client = new MineChatClient();

// One-time: link with a code from /minechat link
await client.LinkAsync("myserver.com:7632", "ABC123");

// Later: reconnect using the stored client UUID
// await client.ConnectAsync("myserver.com:7632", storedClientUuid, pinnedCert);

client.ChatMessageReceived += (_, e) =>
    Console.WriteLine($"[{e.Message.Source}] {e.Message.Content}");

await client.SendChatMessageAsync("Hello from MineChat!", "commonmark");

The client manages TLS handshake, certificate pinning, the auth flow, and keep-alive for you.

Implementing a server

Use MineChatConnection to read and write framed, compressed packets over any Stream -- a server is just a TCP/TLS listener plus a connection loop... or you can use your own connection and implement IMineChatConnection.

Example server
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using MineChat.Protocol;

await using var listener = new TcpListener(IPAddress.Any, 7632);
listener.Start();
var serverCert = new X509Certificate2("server.pfx", "cert-password");

while (true)
{
    var tcp = await listener.AcceptTcpClientAsync();
    _ = HandleClientAsync(tcp, serverCert);
}

static async Task HandleClientAsync(TcpClient tcp, X509Certificate2 cert)
{
    var ssl = new SslStream(tcp.GetStream());
    try {
        await ssl.AuthenticateAsServerAsync(cert);
    }
    catch
    {
        return;
    }

    await using var conn = new MineChatConnection(ssl);

    MineChatPacket? packet;
    while ((packet = await conn.ReadPacketAsync()) != null)
    {
        switch (packet.PacketType)
        {
            case PacketTypes.LINK:
                var link = (LinkPayload)packet.Payload;
                await conn.SendPacketAsync(
                    new MineChatPacket(PacketTypes.LINK_OK,
                        new LinkOkPayload(minecraftUuid: link.ClientUuid)));
                break;

            case PacketTypes.CAPABILITIES:
                await conn.SendPacketAsync(
                    new MineChatPacket(PacketTypes.AUTH_OK, new AuthOkPayload()));
                break;

            case PacketTypes.CHAT_MESSAGE:
                var chat = (ChatMessagePayload)packet.Payload;
                Console.WriteLine($"[{chat.Source}] {chat.Content}");
                break;

            case PacketTypes.PING:
                var ping = (PingPayload)packet.Payload;
                await conn.SendPacketAsync(
                    new MineChatPacket(PacketTypes.PONG,
                        new PongPayload(ping.TimestampMs)));
                break;
        }
    }
}

MineChatConnection handles framing compression, and CBOR -- so your server logic only deals with typed packets.

Working with text components

CHAT_MESSAGE packets use Minecraft's text component JSON format. The TextComponent type parses those, and TextComponentHelper builds them.

Build a styled component:

using MineChat.Protocol.Networking;
using System.Text.Json;

// Build: { "text": "Hello!", "color": "red", "bold": true }
var json = TextComponentHelper.Create("Hello!", bold: true, color: "red");

Parse an incoming component:

var json = """{"text":"Player","extra":[{"text":" joined","color":"green"}]}""";
var component = TextComponentHelper.Deserialize(json);

// Walk with style inheritance:
var segments = component!.Flatten();
foreach (var seg in segments)
    Console.WriteLine($"\u001b[31m{seg.Text}\u001b[0m"); // ANSI render

var plain = component.GetPlainText();

TextComponent covers the full Minecraft spec (text, translate, score, selector, keybind, nbt) plus clickEvent and hoverEvent. Flatten() resolves style inheritance across nested extra children.

Key types

Namespace Types
MineChat.Protocol MineChatPacket, PacketTypes, PacketPayload, IMineChatConnection, MineChatConnection
MineChat.Protocol.Networking MineChatClient, ConnectionState, ChatMessage, TextComponent
MineChat.Protocol.Framing FrameHandler, ProtocolFrame
MineChat.Protocol.Compression ICompressionHandler, ZstdSharpCompressor, CliCompressor

Protocol spec

See the MineChat specification for the authoritative wire format documentation.

License

MIT

About

.NET implementation of the MineChat protocol

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages