Skip to main content
This example demonstrates how to create a simple Gate plugin from scratch, including plugin initialization, event handling, and command registration.

Overview

This example creates a plugin that:
  • Welcomes players when they join
  • Adds a /ping command to check player latency
  • Listens to server connection events
  • Shows proper plugin structure and initialization

Complete Example

Project Structure

simple-plugin/
├── go.mod
└── main.go

go.mod

Create a new Go module for your plugin:
go.mod
module simple-plugin

go 1.24

require (
	github.com/robinbraemer/event v0.1.1
	go.minekube.com/brigodier v0.0.2
	go.minekube.com/common v0.3.0
	go.minekube.com/gate v0.62.3
)

main.go

main.go
package main

import (
	"context"
	"fmt"

	"github.com/robinbraemer/event"
	"go.minekube.com/brigodier"
	"go.minekube.com/common/minecraft/color"
	"go.minekube.com/common/minecraft/component"
	"go.minekube.com/gate/cmd/gate"
	"go.minekube.com/gate/pkg/command"
	"go.minekube.com/gate/pkg/edition/java/proxy"
)

func main() {
	// Register the plugin with Gate
	proxy.Plugins = append(proxy.Plugins, proxy.Plugin{
		Name: "SimplePlugin",
		Init: func(ctx context.Context, p *proxy.Proxy) error {
			return initPlugin(p)
		},
	})

	// Execute Gate's entrypoint (blocking until shutdown)
	gate.Execute()
}

func initPlugin(p *proxy.Proxy) error {
	// Register commands
	registerCommands(p)

	// Register event listeners
	registerEvents(p)

	return nil
}

func registerCommands(p *proxy.Proxy) {
	// Register a simple /ping command
	p.Command().Register(
		brigodier.Literal("ping").Executes(
			command.Command(func(c *command.Context) error {
				player, ok := c.Source.(proxy.Player)
				if !ok {
					return c.Source.SendMessage(&component.Text{
						Content: "Pong!",
					})
				}

				return player.SendMessage(&component.Text{
					Content: fmt.Sprintf("Pong! Your ping is %s", player.Ping()),
					S:       component.Style{Color: color.Green},
				})
			}),
		),
	)
}

func registerEvents(p *proxy.Proxy) {
	// Subscribe to the PostLogin event to welcome players
	event.Subscribe(p.Event(), 0, func(e *proxy.PostLoginEvent) {
		player := e.Player()
		_ = player.SendMessage(&component.Text{
			Content: fmt.Sprintf("Welcome to the server, %s!", player.Username()),
			S:       component.Style{Color: color.Gold},
		})
	})

	// Subscribe to ServerPostConnect to notify on server switches
	event.Subscribe(p.Event(), 0, func(e *proxy.ServerPostConnectEvent) {
		player := e.Player()
		currentServer := player.CurrentServer()
		if currentServer == nil {
			return
		}

		_ = player.SendMessage(&component.Text{
			Content: fmt.Sprintf("Connected to %s", currentServer.Server().ServerInfo().Name()),
			S:       component.Style{Color: color.Aqua},
		})
	})
}

Building and Running

Initialize the module

go mod init simple-plugin
go mod tidy

Build the plugin

go build -o simple-plugin

Run the plugin

./simple-plugin
Gate will start with your plugin loaded. You can now:
  • Join the proxy to see the welcome message
  • Use /ping to check your latency
  • Connect to backend servers to see connection messages

Configuration

To configure backend servers, create a config.yml file:
config.yml
config:
  bind: 0.0.0.0:25565
  servers:
    lobby:
      address: localhost:25566
    survival:
      address: localhost:25567
  try:
    - lobby

Key Concepts

Plugin Registration

Plugins are registered by appending to proxy.Plugins before calling gate.Execute()

Initialization Hook

The Init function is called after the proxy initializes but before it starts accepting connections

Event System

Use event.Subscribe() to listen to proxy events with a priority value (0 is normal)

Command Registration

Commands are registered using Brigadier’s fluent API via proxy.Command().Register()

Next Steps

The context.Context passed to the Init function is canceled when the proxy shuts down. Use it for graceful cleanup!

Build docs developers (and LLMs) love