Skip to main content

Quick Start

This guide will walk you through creating a simple Minecraft client that connects to a server and a basic server that accepts player connections.

Connecting as a Client

Let’s create a client that connects to a Minecraft server, spawns in the world, and reads packets.
1

Create the Client File

Create a new file called client.go:
client.go
package main

import (
    "fmt"
    "github.com/sandertv/gophertunnel/minecraft"
    "github.com/sandertv/gophertunnel/minecraft/auth"
    "github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)

func main() {
    // Create a minecraft.Dialer with an auth.TokenSource to authenticate
    dialer := minecraft.Dialer{
        TokenSource: auth.TokenSource,
    }
    
    // Dial a connection to the target server
    address := "mco.mineplex.com:19132"
    conn, err := dialer.Dial("raknet", address)
    if err != nil {
        panic(err)
    }
    defer conn.Close()

    // Make the client spawn in the world
    if err := conn.DoSpawn(); err != nil {
        panic(err)
    }
    
    fmt.Println("Connected and spawned successfully!")

    // Read packets in a loop
    for {
        pk, err := conn.ReadPacket()
        if err != nil {
            break
        }

        // Type assert packets to access their data
        switch p := pk.(type) {
        case *packet.Emote:
            fmt.Printf("Emote packet received: %v\n", p.EmoteID)
        case *packet.MovePlayer:
            fmt.Printf("Player %v moved to %v\n", p.EntityRuntimeID, p.Position)
        case *packet.Text:
            fmt.Printf("Chat message: %v\n", p.Message)
        }
    }
}
2

Run the Client

go run client.go
The first time you run this, you’ll be prompted to authenticate with Microsoft/Xbox Live. Follow the authentication flow in your browser.
The auth.TokenSource handles Xbox Live authentication automatically. Tokens are cached for subsequent runs.

Understanding the Client Code

Let’s break down the key components:
ComponentDescription
minecraft.DialerUsed to establish connections to remote servers
TokenSourceHandles Xbox Live authentication
conn.DoSpawn()Completes the login sequence and spawns the player
conn.ReadPacket()Reads the next packet from the server
conn.WritePacket()Sends a packet to the server

Writing Packets

To send packets to the server, use WritePacket():
// Request a larger chunk radius
p := &packet.RequestChunkRadius{
    ChunkRadius: 32,
    MaxChunkRadius: 32,
}
if err := conn.WritePacket(p); err != nil {
    break
}

Creating a Server

Now let’s create a basic server that accepts player connections.
1

Create the Server File

Create a new file called server.go:
server.go
package main

import (
    "fmt"
    "github.com/sandertv/gophertunnel/minecraft"
    "github.com/sandertv/gophertunnel/minecraft/protocol/packet"
)

func main() {
    // Create a ListenConfig with a status provider
    cfg := minecraft.ListenConfig{
        StatusProvider: minecraft.NewStatusProvider("My Gophertunnel Server", "Gophertunnel"),
    }

    // Listen on port 19132
    listener, err := cfg.Listen("raknet", ":19132")
    if err != nil {
        panic(err)
    }
    defer listener.Close()
    
    fmt.Println("Server listening on :19132")

    // Accept connections in a loop
    for {
        c, err := listener.Accept()
        if err != nil {
            return
        }
        conn := c.(*minecraft.Conn)

        // Handle each connection in a goroutine
        go handleConnection(conn)
    }
}

func handleConnection(conn *minecraft.Conn) {
    defer conn.Close()
    
    // Start the game for this client
    worldData := minecraft.GameData{}
    if err := conn.StartGame(worldData); err != nil {
        return
    }
    
    fmt.Printf("Player %s connected\n", conn.IdentityData().DisplayName)

    // Read packets from the client
    for {
        pk, err := conn.ReadPacket()
        if err != nil {
            break
        }

        switch p := pk.(type) {
        case *packet.Emote:
            fmt.Printf("Emote received: %v\n", p.EmoteID)
        case *packet.MovePlayer:
            fmt.Printf("%s moved to %v\n", conn.IdentityData().DisplayName, p.Position)
        case *packet.Text:
            fmt.Printf("<%s> %s\n", conn.IdentityData().DisplayName, p.Message)
            
            // Echo the message back
            response := &packet.Text{
                TextType: packet.TextTypeChat,
                Message:  fmt.Sprintf("You said: %s", p.Message),
            }
            conn.WritePacket(response)
        }
    }
    
    fmt.Printf("Player %s disconnected\n", conn.IdentityData().DisplayName)
}
2

Run the Server

go run server.go
Your server is now running on port 19132! Connect to it from Minecraft using localhost:19132 or your machine’s IP address.
The server will appear in the LAN games list in Minecraft Bedrock Edition. You can also add it manually using the server’s IP address.

Understanding the Server Code

ComponentDescription
minecraft.ListenConfigConfiguration for the server listener
StatusProviderDefines the server name/MOTD shown in the server list
listener.Accept()Waits for and accepts new player connections
conn.StartGame()Sends game data and spawns the player
conn.IdentityData()Returns player identity information (name, UUID, etc.)

Customizing Game Data

The minecraft.GameData struct contains all world and game settings:
worldData := minecraft.GameData{
    WorldName: "My Custom World",
    Difficulty: 2,  // Normal difficulty
    GameType: 1,     // Creative mode
    // ... many more fields available
}
conn.StartGame(worldData)

Building a Proxy

One of the most powerful uses of Gophertunnel is creating a MITM proxy. Here’s the basic pattern:
// Accept client connection
clientConn, _ := listener.Accept()

// Dial to the actual server
serverConn, _ := dialer.Dial("raknet", "target-server.com:19132")

// Forward packets bidirectionally
go func() {
    for {
        pk, _ := clientConn.ReadPacket()
        serverConn.WritePacket(pk)  // Forward to server
    }
}()

go func() {
    for {
        pk, _ := serverConn.ReadPacket()
        clientConn.WritePacket(pk)  // Forward to client
    }
}()
For a complete proxy implementation with proper error handling and game state management, see the main.go file in the Gophertunnel repository.

Next Steps

API Reference

Explore the complete API documentation

Packet Types

Browse all available packet types
Remember: Always use Gophertunnel responsibly and never for malicious purposes.