### Server with Resource Packs Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/11-examples.md This example shows how to start a Minecraft server that loads and serves custom resource packs to clients. Ensure 'textures.zip' and 'behaviors.zip' are in the same directory as the executable. ```go package main import ( "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/resource" "log" ) func main() { // Load resource packs texturePack, err := resource.ReadPath("textures.zip") if err != nil { log.Fatalf("Failed to load texture pack: %v\n", err) } behaviorPack, err := resource.ReadPath("behaviors.zip") if err != nil { log.Fatalf("Failed to load behavior pack: %v\n", err) } log.Printf("Loaded texture pack %s v%s\n", texturePack.UUID, texturePack.Version) log.Printf("Loaded behavior pack %s v%s\n", behaviorPack.UUID, behaviorPack.Version) cfg := minecraft.ListenConfig{ AuthenticationDisabled: false, MaximumPlayers: 50, ResourcePacks: []*resource.Pack{texturePack, behaviorPack}, TexturePacksRequired: true, // Must accept to join } listener, _ := cfg.Listen("raknet", ":19132") defer listener.Close() log.Println("Server listening with custom packs") for { conn, err := listener.Accept() if err != nil { log.Printf("Accept error: %v\n", err) continue } c := conn.(*minecraft.Conn) // Check which packs client accepted accepted := c.ResourcePacks() log.Printf("Player %s accepted %d packs\n", c.IdentityData().DisplayName, len(accepted)) go handleClient(c) } } func handleClient(conn *minecraft.Conn) { defer conn.Close() // ... } ``` -------------------------------- ### Complete GopherTunnel Server Example Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md This example demonstrates a complete GopherTunnel server that listens for incoming Minecraft connections, accepts clients, and handles basic chat messages. It includes setting up the listener, accepting connections, and managing client sessions. ```go package main import ( "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/go-gl/mathgl/mgl32" "log" ) func main() { listener, err := minecraft.Listen("raknet", ":19132") if err != nil { log.Fatal(err) } defer listener.Close() log.Printf("Server listening on %s\n", listener.Addr()) for { conn, err := listener.Accept() if err != nil { log.Printf("accept error: %v\n", err) continue } c := conn.(*minecraft.Conn) go handleClient(c, listener) } } func handleClient(conn *minecraft.Conn, listener *minecraft.Listener) { defer conn.Close() identity := conn.IdentityData() log.Printf("Player %s joined\n", identity.DisplayName) // Send StartGame gameData := minecraft.GameData{ WorldName: "My World", WorldSeed: 12345, Difficulty: 1, EntityUniqueID: 1, EntityRuntimeID: 1, PlayerGameMode: 0, PlayerPosition: mgl32.Vec3{0, 70, 0}, Dimension: 0, } if err := conn.StartGame(gameData); err != nil { log.Printf("start game error: %v\n", err) return } // Wait for spawn if err := conn.DoSpawn(); err != nil { log.Printf("spawn error: %v\n", err) return } log.Printf("Player %s spawned\n", identity.DisplayName) // Handle packets for { pk, err := conn.ReadPacket() if err != nil { log.Printf("read error: %v\n", err) return } switch p := pk.(type) { case *packet.Chat: log.Printf("[%s] %s\n", identity.DisplayName, p.Message) // Broadcast to all clients (simplified) broadcast := &packet.Chat{ Message: identity.DisplayName + ": " + p.Message, TextType: packet.TextTypeChat, } conn.WritePacket(broadcast) conn.Flush() } } } ``` -------------------------------- ### Custom Configuration Example Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/05-auth-api.md Example of how to create and use a custom OAuth2 configuration. ```APIDOC ## Custom Configuration Example ### Description Example of how to create and use a custom OAuth2 configuration. ### Code Example ```go customConfig := auth.Config{ ClientID: "your-app-id", ClientSecret: "your-app-secret", Scopes: []string{"Xbox.signin", "offline_access"}, } token, err := customConfig.RequestLiveToken() ``` ``` -------------------------------- ### Examples and Usage Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/MANIFEST.txt Reference to working code examples and usage patterns. ```APIDOC ## Examples (11-examples.md) ### Description Provides working code examples for various GopherTunnel functionalities. ### Code Examples - **100+ working code examples**: Covering client-side, server-side, packet handling, and authentication. - **5 complete working examples**: Demonstrating key features and integration patterns. ``` -------------------------------- ### Custom OAuth2 Configuration Example Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/05-auth-api.md Example of setting up a custom OAuth2 configuration with specific client ID, client secret, and scopes. This configuration can then be used to request a live token. ```go customConfig := auth.Config{ ClientID: "your-app-id", ClientSecret: "your-app-secret", Scopes: []string{"Xbox.signin", "offline_access"}, } token, err := customConfig.RequestLiveToken() ``` -------------------------------- ### Example: Basic Status Provider Usage Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Demonstrates how to create and use a basic status provider with a Gophertunnel listener. ```go provider := minecraft.NewStatusProvider("My Server", "Welcome!") cfg := minecraft.ListenConfig{ StatusProvider: provider, } listener, _ := cfg.Listen("raknet", ":19132") ``` -------------------------------- ### Go Chat Packet Example Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/08-packets-reference.md Example of creating and sending a chat packet. Ensure the packet type and message content are correctly set. ```go msg := &packet.Chat{ Message: "Hello world!", TextType: packet.TextTypeChat, } conn.WritePacket(msg) conn.Flush() ``` -------------------------------- ### Packet Batching Example Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/README.md Demonstrates how packets are buffered until a flush rate or an explicit flush call. Use WritePacket to buffer and Flush to send immediately. ```go conn.WritePacket(&packet1) conn.WritePacket(&packet2) conn.WritePacket(&packet3) // All sent together after 50ms, or: conn.Flush() // Send immediately ``` -------------------------------- ### Example Packet Structure and Methods Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/08-packets-reference.md Illustrates a generic packet structure with fields and the required methods (ID, Marshal, Unmarshal) that concrete packet types must implement. ```go type ExamplePacket struct { Field1 string Field2 int32 Field3 []byte } func (pk *ExamplePacket) ID() uint32 { return IDExamplePacket // Unique packet ID } func (pk *ExamplePacket) Marshal(io protocol.IO) error { return nil // Encode fields } func (pk *ExamplePacket) Unmarshal(io protocol.IO) error { return nil // Decode fields } ``` -------------------------------- ### Complete GopherTunnel Server Example Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md A full implementation of a GopherTunnel server that listens for connections, handles client joins, and processes incoming packets. Ensure to handle authentication and player limits appropriately. ```go package main import ( "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/protocol/login" "log" ) func main() { cfg := minecraft.ListenConfig{ AuthenticationDisabled: false, // Verify Xbox Live MaximumPlayers: 100, } listener, err := cfg.Listen("raknet", ":19132") if err != nil { log.Fatal(err) } defer listener.Close() log.Printf("Server listening on %s\n", listener.Addr()) for { conn, err := listener.Accept() if err != nil { log.Printf("accept error: %v\n", err) continue } c := conn.(*minecraft.Conn) identity := c.IdentityData() log.Printf("Player %s (XUID: %s) joined\n", identity.DisplayName, identity.XUID) go handleClient(c) } } func handleClient(conn *minecraft.Conn) { defer conn.Close() // Handle packets for { pk, err := conn.ReadPacket() if err != nil { return } // Process packet log.Printf("Received packet: %T\n", pk) } } ``` -------------------------------- ### StartGameContext: Start Game with Context Cancellation Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Initiates a game session using a provided context, allowing for cancellation of the operation. Ideal for integrating with existing cancellation mechanisms. ```go func (conn *Conn) StartGameContext(ctx context.Context, data GameData) error ``` -------------------------------- ### Dial functions (Dial, DialTimeout, DialContext) Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/MANIFEST.txt Provides examples of different ways to establish a client connection. DialContext is preferred for managing timeouts and cancellation. ```go package main import ( "context" "log" "time" "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/auth" ) func main() { // Placeholder for authentication data xblToken, _ := auth.XBLTokenFromJSON("xbl_token.json") liveToken, _ := auth.LiveTokenFromJSON("live_token.json") identityData, _ := auth.NewIdentityData(xblToken, liveToken) // 1. Using DialContext with a timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() conn1, err := minecraft.DialContext(ctx, "proxy.example.com:19132", identityData) if err != nil { log.Printf("DialContext failed: %v\n", err) } else { log.Println("DialContext successful") conn1.Close() } // 2. Using DialTimeout (less flexible than DialContext) conn2, err := minecraft.DialTimeout("proxy.example.com:19132", identityData, 10*time.Second) if err != nil { log.Printf("DialTimeout failed: %v\n", err) } else { log.Println("DialTimeout successful") conn2.Close() } // 3. Using Dial (no explicit timeout, relies on context cancellation if provided) // For simplicity, we'll use a background context here, but in a real app, use a cancellable context. conn3, err := minecraft.Dial(context.Background(), "proxy.example.com:19132", identityData) if err != nil { log.Printf("Dial failed: %v\n", err) } else { log.Println("Dial successful") conn3.Close() } } ``` -------------------------------- ### Server-Side Functions Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/README.md Functions for starting and managing a Minecraft server listener. ```APIDOC ## Server-Side Functions ### `Listen()` Start a new Minecraft server listener on a default network interface. ### `(ListenConfig).Listen()` Start a new Minecraft server listener using the configurations defined in `ListenConfig`. ### `(ListenConfig).ListenNetwork(network string)` Start a new Minecraft server listener on a specified network interface. ### `(Listener).Accept()` Accept an incoming client connection on the server. ### `(Listener).Disconnect(conn Conn)` Disconnect a specific client connection from the server. ``` -------------------------------- ### Sending RequestNetworkSettings Packet Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/08-packets-reference.md Example of how to create and send a RequestNetworkSettings packet from the client to the server as part of the initial connection sequence. ```go req := &packet.RequestNetworkSettings{ ClientProtocol: protocol.CurrentProtocol, } conn.WritePacket(req) ``` -------------------------------- ### Start Minecraft Server in Offline Mode (Go) Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/11-examples.md Use this configuration to start a Minecraft server that does not require Xbox Live authentication. Set AuthenticationDisabled to true. ```go package main import ( "github.com/sandertv/gophertunnel/minecraft" "log" ) func main() { // Server without authentication cfg := minecraft.ListenConfig{ AuthenticationDisabled: true, // No Xbox Live verification MaximumPlayers: 100, } listener, _ := cfg.Listen("raknet", ":19132") defer listener.Close() log.Println("Server running in offline mode") log.Println("Players can join without Xbox Live account") for { conn, _ := listener.Accept() c := conn.(*minecraft.Conn) identity := c.IdentityData() if c.Authenticated() { log.Printf("Authenticated player: %s (XUID: %s)\n", identity.DisplayName, identity.XUID) } else { log.Printf("Unauthenticated player: %s\n", identity.DisplayName) } go handleClient(c) } } func handleClient(conn *minecraft.Conn) { defer conn.Close() // ... } ``` -------------------------------- ### Complete GopherTunnel Client Example Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md A full client implementation connecting to a Minecraft server, handling authentication, reading packets, and sending chat responses. Ensure you replace 'mc.example.com:19132' with a valid server address. ```go package main import ( "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/auth" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "log" ) func main() { dialer := minecraft.Dialer{ TokenSource: auth.TokenSource, // Device auth } conn, err := dialer.Dial("raknet", "mc.example.com:19132") if err != nil { log.Fatal(err) } defer conn.Close() identity := conn.IdentityData() log.Printf("Connected as %s\n", identity.DisplayName) // Read packets for { pk, err := conn.ReadPacket() if err != nil { log.Printf("read error: %v\n", err) break } switch p := pk.(type) { case *packet.Chat: log.Printf("[Chat] %s\n", p.Message) // Send response response := &packet.Chat{ Message: "Hello!", TextType: packet.TextTypeChat, } conn.WritePacket(response) conn.Flush() } } } ``` -------------------------------- ### Accept Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Blocks until a client connects and completes the login sequence. It handles the initial network negotiation, authentication, and game start. ```APIDOC ## Accept ### Description Blocks until a client connects and completes the login sequence. It handles the initial network negotiation, authentication, and game start. ### Method `Accept` ### Return - `net.Conn` — connected client (cast to `*minecraft.Conn`) - `error` — nil on success, non-nil if listener is closed or login fails ### Example ```go listener, _ := minecraft.Listen("raknet", ":19132") def listener.Close() for { conn, err := listener.Accept() if err != nil { break } c := conn.(*minecraft.Conn) identity := c.IdentityData() log.Printf("Player %s joined\n", identity.DisplayName) go handleClient(c) } ``` ``` -------------------------------- ### Package-Level Listen Function Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Creates a Listener with default configuration using the string network ID. This is the primary way to start listening for incoming connections. ```APIDOC ## Listen ### Description Creates a Listener with default configuration using the string network ID. ### Signature ```go func Listen(network, address string) (*Listener, error) ``` ### Parameters #### Path Parameters - **network** (string) - Required - Network type, typically "raknet" - **address** (string) - Required - Bind address in form "host:port" or ":port" for all interfaces ### Return - `*Listener` — listening Minecraft listener, or `error` if bind fails ### Errors - Invalid network ID (not "raknet" or registered custom network) - Port already in use - Insufficient permissions ### Example ```go listener, err := minecraft.Listen("raknet", ":19132") if err != nil { log.Fatalf("failed to listen: %v", err) } defer listener.Close() for { conn, _ := listener.Accept() go handleClient(conn) } ``` ``` -------------------------------- ### Serve Custom Resource Packs Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/10-resource-packs-api.md Configure and start a Minecraft server that serves custom resource packs to connecting players. Ensure the zip files for custom textures and behaviors are present in the same directory as the executable. ```go package main import ( "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/resource" "log" "os" ) func main() { // Load custom packs customTextures, _ := resource.ReadPath("custom_textures.zip") customBehaviors, _ := resource.ReadPath("custom_behaviors.zip") cfg := minecraft.ListenConfig{ ResourcePacks: []*resource.Pack{customTextures, customBehaviors}, TexturePacksRequired: true, FetchResourcePacks: func(identity, clientData, current) []*resource.Pack { // All players get the same packs return current }, } listener, err := cfg.Listen("raknet", ":19132") if err != nil { log.Fatal(err) } defer listener.Close() log.Println("Server listening, serving custom packs") for { conn, err := listener.Accept() if err != nil { log.Printf("Accept error: %v\n", err) continue } c := conn.(*minecraft.Conn) accepted := c.ResourcePacks() log.Printf("Player accepted %d packs\n", len(accepted)) go handleClient(c) } } func handleClient(conn *minecraft.Conn) { defer conn.Close() // ... } ``` -------------------------------- ### Handle Unknown Packets Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/08-packets-reference.md When AllowUnknownPackets is true, unknown packets are returned as a specific struct. This example shows how to check for and process these unknown packets. ```go type Unknown struct { PacketID uint32 Content []byte } ``` ```go pk, _ := conn.ReadPacket() if unknownPk, ok := pk.(*packet.Unknown); ok { log.Printf("Unknown packet ID: %d, Size: %d bytes\n", unknownPk.PacketID, len(unknownPk.Content)) } ``` -------------------------------- ### Token Persistence with Auto-Refresh Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/05-auth-api.md This example shows how to load a saved OAuth2 token from a file, or request a new one if it doesn't exist. It then uses `auth.RefreshTokenSource` to automatically refresh the token. ```go package main import ( "encoding/json" "os" "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/auth" "golang.org/x/oauth2" "log" ) func main() { // Load or create token var token *oauth2.Token data, err := os.ReadFile("token.json") if err == nil { json.Unmarshal(data, &token) log.Println("Loaded saved token") } else { // Get new token token, err = auth.RequestLiveToken() if err != nil { log.Fatal(err) } // Save for next time data, _ := json.Marshal(token) os.WriteFile("token.json", data, 0600) } // Use with auto-refresh tokenSrc := auth.RefreshTokenSource(token) dialer := minecraft.Dialer{ TokenSource: tokenSrc, } conn, err := dialer.Dial("raknet", "mc.example.com:19132") if err != nil { log.Fatal(err) } defer conn.Close() } ``` -------------------------------- ### Handle Packet Write and Flush Errors Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/07-errors.md This example demonstrates how to handle errors that may occur during packet writing or flushing, such as encoding issues or connection closures. ```go chat := &packet.Chat{Message: "Hello"} if err := conn.WritePacket(chat); err != nil { log.Printf("Failed to send packet: %v\n", err) return } if err := conn.Flush(); err != nil { log.Printf("Flush failed: %v\n", err) return } ``` -------------------------------- ### StartGameTimeout: Start Game with Custom Timeout Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Sends a StartGame packet with a specified maximum duration for the client to acknowledge spawning. Useful for controlling wait times. ```go func (conn *Conn) StartGameTimeout(data GameData, timeout time.Duration) error ``` -------------------------------- ### Sending PlayStatus Packet for Player Spawn Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/08-packets-reference.md Example of how a client can send a PlayStatus packet with the PlayerSpawn status to indicate that the player has successfully spawned in the game world. ```go // Client sends spawn complete spawn := &packet.PlayStatus{Status: 3} // PlayerSpawn conn.WritePacket(spawn) ``` -------------------------------- ### Client with Token Persistence Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/11-examples.md Implement client-side token persistence to automatically refresh authentication tokens for connecting to a Minecraft server. This example saves and loads tokens from 'token.json'. Ensure necessary imports for JSON, OAuth2, and GopherTunnel authentication are included. ```go package main import ( "encoding/json" "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/auth" "golang.org/x/oauth2" "log" "os" "time" ) const tokenFile = "token.json" func main() { var token *oauth2.Token // Try to load saved token if data, err := os.ReadFile(tokenFile); err == nil { json.Unmarshal(data, &token) log.Println("Loaded saved token") } else { // Get new token var err error token, err = auth.RequestLiveToken() if err != nil { log.Fatal(err) } // Save token for next time data, _ := json.Marshal(token) os.WriteFile(tokenFile, data, 0600) log.Println("Saved token for future use") } // Create auto-refreshing token source tokenSrc := auth.RefreshTokenSource(token) // Connect with token dialer := minecraft.Dialer{ TokenSource: tokenSrc, } conn, err := dialer.Dial("raknet", "server.example.com:19132") if err != nil { log.Fatal(err) } defer conn.Close() log.Printf("Connected as %s\n", conn.IdentityData().DisplayName) // Read packets... for { pk, err := conn.ReadPacket() if err != nil { break } log.Printf("Received: %T\n", pk) } } ``` -------------------------------- ### Accept New Minecraft Connections Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Blocks until a client connects and completes the login sequence. Handles initial network settings, authentication, resource pack negotiation, and game start. Returns the connected client or an error if the listener is closed or login fails. ```go listener, _ := minecraft.Listen("raknet", ":19132") def listener.Close() for { conn, err := listener.Accept() if err != nil { break } c := conn.(*minecraft.Conn) identity := c.IdentityData() log.Printf("Player %s joined\n", identity.DisplayName) go handleClient(c) } ``` -------------------------------- ### Listen functions (Listen, ListenNetwork) Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/MANIFEST.txt Demonstrates how to create a server listener. Listen is a convenience function, while ListenNetwork allows more configuration. ```go package main import ( "context" "log" "github.com/sandertv/gophertunnel/minecraft" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // 1. Using Listen (listens on all interfaces, default port) listener1, err := minecraft.Listen("0.0.0.0:19132") if err != nil { log.Fatalf("Listen failed: %v\n", err) } log.Println("Listener 1 active on 0.0.0.0:19132") go func() { for { _, err := listener1.Accept(ctx) if err != nil { return } } }() // 2. Using ListenNetwork with specific configuration listener2, err := minecraft.ListenNetwork("tcp", "127.0.0.1:19133") if err != nil { log.Fatalf("ListenNetwork failed: %v\n", err) } log.Println("Listener 2 active on 127.0.0.1:19133") go func() { for { _, err := listener2.Accept(ctx) if err != nil { return } } }() // Keep the main goroutine running <-ctx.Done() listener1.Close() listener2.Close() } ``` -------------------------------- ### Simple Server Implementation Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/MANIFEST.txt Provides a basic server implementation that listens for incoming client connections. Handles connection acceptance and basic logging. ```go package main import ( "context" "log" "github.com/sandertv/gophertunnel/minecraft" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Create a listener that listens on all interfaces on port 19132 listener, err := minecraft.Listen("0.0.0.0:19132") if err != nil { log.Fatalf("failed to create listener: %v", err) } defer listener.Close() log.Println("Server listening on 0.0.0.0:19132") for { // Accept a new connection conn, err := listener.Accept(ctx) if err != nil { log.Printf("failed to accept connection: %v\n", err) continue } log.Printf("Client connected: %s\n", conn.RemoteAddr().String()) // Handle the connection in a new goroutine go handleConnection(ctx, conn) } } func handleConnection(ctx context.Context, conn *minecraft.Conn) { defer conn.Close() // Keep the connection alive until the context is cancelled <-ctx.Done() } ``` -------------------------------- ### Load Resource Packs at Startup Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/10-resource-packs-api.md Loads all resource packs from a specified directory during application initialization. Handles errors during loading and logs failures for individual packs. ```go var globalPacks []*resource.Pack func init() { var err error globalPacks, err = loadResourcePacks("./packs") if err != nil { log.Fatal(err) } } func loadResourcePacks(dir string) ([]*resource.Pack, error) { files, _ := os.ReadDir(dir) var packs []*resource.Pack for _, file := range files { if strings.HasSuffix(file.Name(), ".zip") { path := filepath.Join(dir, file.Name()) pack, err := resource.ReadPath(path) if err != nil { log.Printf("Failed to load %s: %v\n", file.Name(), err) continue } packs = append(packs, pack) } } return packs, nil } // Use in listener cfg := minecraft.ListenConfig{ ResourcePacks: globalPacks, } listener, _ := cfg.Listen("raknet", ":19132") ``` -------------------------------- ### Create Minecraft Server (Go) Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/README.md Create a Minecraft server instance listening on a specific address. Configure server settings like authentication and maximum players. New client connections are handled in a separate goroutine. ```go cfg := minecraft.ListenConfig{ AuthenticationDisabled: false, MaximumPlayers: 100, } listener, err := cfg.Listen("raknet", ":19132") if err != nil { log.Fatal(err) } deferr listener.Close() for { conn, err := listener.Accept() if err != nil { break } c := conn.(*minecraft.Conn) go handleClient(c) } ``` -------------------------------- ### Get Listener Address Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Retrieves the network address to which the listener is currently bound. ```go listener, _ := minecraft.Listen("raknet", ":19132") log.Println("Listening on", listener.Addr()) ``` -------------------------------- ### Listen with Static Resource Packs Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Configure the listener to serve static resource packs. Clients are required to accept these packs. ```go pack, _ := resource.ReadPath("textures.zip") cfg := minecraft.ListenConfig{ ResourcePacks: []*resource.Pack{pack}, TexturePacksRequired: true, // Clients must accept } listener, _ := cfg.Listen("raknet", ":19132") ``` -------------------------------- ### Get Client Latency Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Retrieves the estimated round-trip latency to the connected server. The result is in milliseconds. ```go ms := conn.Latency().Milliseconds() log.Printf("Client latency: %d ms\n", ms) ``` -------------------------------- ### Get Local Connection Address Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Returns the local network address of the established GopherTunnel connection. ```go func (conn *Conn) LocalAddr() net.Addr ``` -------------------------------- ### Get Current Player Count Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Returns the total number of players currently connected to the server through this listener. ```go count := listener.PlayerCount() if count >= listener.MaximumPlayers { log.Println("Server is full") } ``` -------------------------------- ### Get Client Chunk Radius Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Returns the chunk radius (render distance) that the client has requested from the server. ```go conn.ChunkRadius() ``` -------------------------------- ### Config.RefreshTokenSource for Custom Configuration Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/05-auth-api.md Creates an auto-refreshing token source for a custom OAuth2 configuration, starting from an existing token. ```go func (conf Config) RefreshTokenSource(t *oauth2.Token) oauth2.TokenSource ``` -------------------------------- ### Add Resource Packs to Listener Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/10-resource-packs-api.md Demonstrates how to initialize a listener with an empty resource pack list and then add packs dynamically. Packs can also be removed later using their UUID. ```go cfg := minecraft.ListenConfig{ ResourcePacks: []*resource.Pack{}, // Start empty } listener, _ := cfg.Listen("raknet", ":19132") // Load packs texturepack, _ := resource.ReadPath("textures.zip") behaviorpack, _ := resource.ReadPath("behaviors.zip") // Add to running listener listener.AddResourcePack(texturepack) listener.AddResourcePack(behaviorpack) // Remove later listener.RemoveResourcePack(texturepack.UUID) ``` -------------------------------- ### Listen with Custom Compression Per Protocol Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Configure the listener with custom compression logic based on the protocol version. Newer protocol versions will use Snappy compression. ```go cfg := minecraft.ListenConfig{ Compression: packet.DefaultCompression, // Base algorithm CompressionSelector: func(proto Protocol) packet.Compression { if proto.ID() >= 685 { // Use Snappy for newer versions return snappyCompression } return packet.DefaultCompression }, } listener, _ := cfg.Listen("raknet", ":19132") ``` -------------------------------- ### Create Listener with Default Configuration Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Use this function to create a new Listener with default settings. It binds to the specified network and address. ```go func Listen(network, address string) (*Listener, error) ``` ```go listener, err := minecraft.Listen("raknet", ":19132") if err != nil { log.Fatalf("failed to listen: %v", err) } defer listener.Close() for { conn, _ := listener.Accept() go handleClient(conn) } ``` -------------------------------- ### Get Packet by ID Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/08-packets-reference.md Retrieve a specific packet from the pool using its ID. Handles potential errors if the packet is not found. ```go pool := conn.Proto().Packets(false) // false = server pool pk, err := pool.Packet(packet.IDChat) if err != nil { log.Println("Packet not in pool") } ``` -------------------------------- ### Server with Resource Packs Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/MANIFEST.txt Configures a server to distribute resource packs to connecting clients. Requires resource packs to be placed in a specific directory. ```go package main import ( "context" "log" "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/resource" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Load resource packs from the "resource_packs" directory packs, err := resource.LoadPacks("resource_packs") if err != nil { log.Fatalf("failed to load resource packs: %v", err) } listener, err := minecraft.ListenConfig{ ResourcePacks: packs, }.Listen("0.0.0.0:19132") if err != nil { log.Fatalf("failed to create listener: %v", err) } defer listener.Close() log.Println("Server listening on 0.0.0.0:19132 with resource packs") for { conn, err := listener.Accept(ctx) if err != nil { log.Printf("failed to accept connection: %v\n", err) continue } log.Printf("Client connected: %s\n", conn.RemoteAddr().String()) go handleConnection(ctx, conn) } } func handleConnection(ctx context.Context, conn *minecraft.Conn) { defer conn.Close() <-ctx.Done() } ``` -------------------------------- ### Get Protocol Implementation Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Obtain the protocol implementation used for the current connection. This handler is responsible for managing the specific protocol version. ```go proto := conn.Proto() ``` -------------------------------- ### Client with Token Persistence Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/MANIFEST.txt Shows how to persist authentication tokens to disk for reuse. This avoids the need to re-authenticate on every client startup. ```go package main import ( "context" "log" "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/auth" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Attempt to load existing tokens from JSON files xblToken, err := auth.XBLTokenFromJSON("xbl_token.json") if err != nil { log.Printf("could not load XBL token, performing auth flow: %v\n", err) // Perform the Xbox Live authentication flow to get a new token // This typically involves user interaction or obtaining credentials // For demonstration, we'll assume it's handled elsewhere or fails here log.Fatal("XBL authentication required") } liveToken, err := auth.LiveTokenFromJSON("live_token.json") if err != nil { log.Printf("could not load Live token, performing auth flow: %v\n", err) // Perform the Live authentication flow log.Fatal("Live authentication required") } // Create identity data from the loaded tokens identityData, err := auth.NewIdentityData(xblToken, liveToken) if err != nil { log.Fatalf("failed to create identity data: %v", err) } // Dial the server conn, err := minecraft.Dial(ctx, "proxy.example.com:19132", identityData) if err != nil { log.Fatalf("failed to dial: %v", err) } defer conn.Close() log.Println("Successfully connected with persistent tokens!") <-ctx.Done() } ``` -------------------------------- ### Simple Minecraft Server Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/11-examples.md Sets up a basic Minecraft server that listens for connections, accepts players, and handles chat and movement packets. Requires `gophertunnel` and `mathgl` packages. ```go package main import ( "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/go-gl/mathgl/mgl32" "log" ) func main() { cfg := minecraft.ListenConfig{ AuthenticationDisabled: false, // Verify Xbox Live MaximumPlayers: 100, } listener, err := cfg.Listen("raknet", ":19132") if err != nil { log.Fatalf("Listen failed: %v\n", err) } defer listener.Close() log.Printf("Server listening on %s\n", listener.Addr()) log.Println("Players can now connect") for { // Wait for a client to connect and complete login conn, err := listener.Accept() if err != nil { log.Printf("Accept error: %v\n", err) continue } c := conn.(*minecraft.Conn) identity := c.IdentityData() log.Printf("Player %s joined (XUID: %s)\n", identity.DisplayName, identity.XUID) // Handle each player in a goroutine go handlePlayer(c) } } func handlePlayer(conn *minecraft.Conn) { defer conn.Close() identity := conn.IdentityData() // Send StartGame to spawn the player gameData := minecraft.GameData{ WorldName: "Minecraft Server", Difficulty: 1, EntityUniqueID: 1, EntityRuntimeID: 1, PlayerGameMode: 0, // Survival PlayerPosition: mgl32.Vec3{0, 70, 0}, Pitch: 0, Yaw: 0, Dimension: 0, // Overworld } if err := conn.StartGame(gameData); err != nil { log.Printf("StartGame failed: %v\n", err) return } // Wait for player to spawn if err := conn.DoSpawn(); err != nil { log.Printf("DoSpawn failed: %v\n", err) return } log.Printf("Player %s spawned\n", identity.DisplayName) // Read packets from the player for { pk, err := conn.ReadPacket() if err != nil { log.Printf("Read error from %s: %v\n", identity.DisplayName, err) return } switch p := pk.(type) { case *packet.Chat: log.Printf("[%s] %s\n", identity.DisplayName, p.Message) // Broadcast to all players (simplified - single player) broadcast := &packet.Chat{ Message: identity.DisplayName + ": " + p.Message, TextType: packet.TextTypeChat, } conn.WritePacket(broadcast) conn.Flush() case *packet.MovePlayer: // Player moved - could validate here log.Printf("%s moved to (%.1f, %.1f, %.1f)\n", identity.DisplayName, p.Position.X, p.Position.Y, p.Position.Z) } } } ``` -------------------------------- ### Listen with Dynamic Resource Packs Per Client Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Configure the listener to dynamically fetch resource packs based on client identity. This allows for custom pack assignments. ```go cfg := minecraft.ListenConfig{ FetchResourcePacks: func(identity login.IdentityData, client login.ClientData, current []*resource.Pack) []*resource.Pack { // Select packs based on client identity if identity.XUID == "admin_xuid" { // Give admin only a special pack return []*resource.Pack{adminPack} } return current // Use default packs }, } listener, _ := cfg.Listen("raknet", ":19132") ``` -------------------------------- ### Get Remote Connection Address Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Returns the remote network address of the GopherTunnel connection, which could be either the client's or the server's address. ```go func (conn *Conn) RemoteAddr() net.Addr ``` -------------------------------- ### Get Accepted Resource Packs Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Retrieves the list of resource packs that were successfully negotiated and accepted by the client. This method is only valid after the login sequence has completed. ```go func (conn *Conn) ResourcePacks() []*resource.Pack ``` -------------------------------- ### Get Player Identity Data Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Retrieve the identity data of the connected player, including their UUID, XUID, and username. This is useful for identifying and logging players. ```go identity := conn.IdentityData() log.Printf("Player: %s, UUID: %s, XUID: %s\n", identity.DisplayName, identity.Identity, identity.XUID) ``` -------------------------------- ### Listen with Default Compression Configuration Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Configure the listener to use default compression for packets exceeding a specified threshold. Packets larger than 256 bytes will be compressed. ```go cfg := minecraft.ListenConfig{ CompressionThreshold: 256, // Compress packets > 256 bytes } listener, _ := cfg.Listen("raknet", ":19132") ``` -------------------------------- ### Adding Packs to Listener Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/10-resource-packs-api.md Demonstrates how to initialize a listener with an empty resource pack list and then add packs dynamically to a running listener. It also shows how to remove packs later using their UUID. ```APIDOC ## Adding Packs to Listener ### Description This example shows how to start with an empty resource pack configuration and add packs to a running listener. Packs can also be removed later. ### Method ```go cfg := minecraft.ListenConfig{ ResourcePacks: []*resource.Pack{}, // Start empty } listener, _ := cfg.Listen("raknet", ":19132") // Load packs texturepack, _ := resource.ReadPath("textures.zip") behaviorpack, _ := resource.ReadPath("behaviors.zip") // Add to running listener listener.AddResourcePack(texturepack) listener.AddResourcePack(behaviorpack) // Remove later listener.RemoveResourcePack(texturepack.UUID) ``` ``` -------------------------------- ### Simple Client Connection with Xbox Live Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/MANIFEST.txt Demonstrates establishing a basic client connection using Xbox Live authentication. Ensure necessary authentication tokens are available. ```go package main import ( "context" "log" "github.com/sandertv/gophertunnel/minecraft" "github.com/sandertv/gophertunnel/minecraft/auth" ) func main() { ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Obtain an XBLToken and a LiveToken (e.g. from a stored file or by performing the auth flow) xblToken, err := auth.XBLTokenFromJSON("xbl_token.json") if err != nil { log.Fatalf("failed to read XBL token: %v", err) } liveToken, err := auth.LiveTokenFromJSON("live_token.json") if err != nil { log.Fatalf("failed to read Live token: %v", err) } // Authenticate with Xbox Live identityData, err := auth.NewIdentityData(xblToken, liveToken) if err != nil { log.Fatalf("failed to create identity data: %v", err) } // Dial a server using the obtained identity data conn, err := minecraft.Dial(ctx, "proxy.example.com:19132", identityData) if err != nil { log.Fatalf("failed to dial: %v", err) } defer conn.Close() log.Println("Successfully connected!") // Keep the connection open until context is cancelled <-ctx.Done() } ``` -------------------------------- ### Create Listener with Custom Configuration Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Creates a Listener using a specific ListenConfig. This allows for customization of settings like authentication and maximum players. ```go func (cfg ListenConfig) Listen(network, address string) (*Listener, error) ``` ```go cfg := minecraft.ListenConfig{ AuthenticationDisabled: true, MaximumPlayers: 100, } listener, err := cfg.Listen("raknet", ":19132") ``` -------------------------------- ### Server-Side Resource Pack Download Protocol (Go) Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/10-resource-packs-api.md Initiates the resource pack download process on the server by sending a `ResourcePacksInfo` packet. This is typically handled automatically by gophertunnel during client acceptance. ```go // In Listener.Accept() or after client login // 1. Send ResourcePacksInfo resourcePacksInfo := &packet.ResourcePacksInfo{ MustAccept: true, ResourcePacks: []packet.ResourcePack{ { UUID: pack.UUID, Version: pack.Version, Size: int64(len(pack.Data)), ContentKey: "", // Optional content key SubPackName: "", ContentIdentity: "", HasScripts: false, }, }, } conn.WritePacket(resourcePacksInfo) // 2. Wait for ResourcePackClientResponse // 3. Send ResourcePackDataInfo for each pack // 4. Send ResourcePackChunkData (multiple packets) // 5. Wait for ResourcePackChunkRequest ACKs ``` -------------------------------- ### Configure Compression Threshold Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/README.md Shows how to configure the compression threshold for packets. Packets larger than the threshold will be compressed. ```go cfg := minecraft.ListenConfig{ CompressionThreshold: 512, // Compress >512 bytes } listener, _ := cfg.Listen("raknet", ":19132") ``` -------------------------------- ### Get Connection Context Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Returns the `context.Context` associated with the GopherTunnel connection. This context is automatically cancelled when the connection is closed, allowing for graceful shutdown and cancellation of ongoing operations. ```go func (conn *Conn) Context() context.Context ``` ```go select { case <-conn.Context().Done(): log.Println("Connection closed") case data := <-otherChan: log.Println(data) } ``` -------------------------------- ### Get Player Client Data Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Retrieve client data such as device information, locale, and skin ID. Note that this data cannot be trusted in untrusted environments as it can be modified by players. ```go clientData := conn.ClientData() log.Printf("Device OS: %d, Language: %s, Skin: %s\n", clientData.DeviceOS, clientData.LanguageCode, clientData.SkinID) ``` -------------------------------- ### StartGame: Send Game State and Wait for Spawn Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/04-conn-api.md Sends a StartGame packet to the client and blocks until the client acknowledges spawning. Use this when the default request timeout is acceptable. ```go func (conn *Conn) StartGame(data GameData) error ``` ```go gameData := minecraft.GameData{ WorldName: "Minecraft", Difficulty: 1, PlayerGameMode: 0, // Survival EntityUniqueID: 1, EntityRuntimeID: 1, PlayerPosition: mgl32.Vec3{0, 70, 0}, Dimension: 0, } if err := conn.StartGame(gameData); err != nil { log.Printf("failed to start game: %v\n", err) } ``` -------------------------------- ### NewStatusProvider Function Source: https://github.com/sandertv/gophertunnel/blob/master/_autodocs/03-listener-api.md Creates a basic server status provider with a given server name and message of the day. ```go func NewStatusProvider(serverName, motd string) ServerStatusProvider ```