package main
import (
"context"
"fmt"
"strings"
"github.com/robinbraemer/event"
"go.minekube.com/common/minecraft/color"
"go.minekube.com/common/minecraft/component"
"go.minekube.com/gate/cmd/gate"
"go.minekube.com/gate/pkg/edition/java/proxy"
)
func main() {
proxy.Plugins = append(proxy.Plugins, proxy.Plugin{
Name: "EventListener",
Init: func(ctx context.Context, p *proxy.Proxy) error {
registerEventListeners(p)
return nil
},
})
gate.Execute()
}
func registerEventListeners(p *proxy.Proxy) {
// Get the event manager
mgr := p.Event()
// Priority 0 is normal, higher numbers = higher priority
const normalPriority = 0
const highPriority = 100
// PreLogin - Validate logins before authentication
event.Subscribe(mgr, normalPriority, handlePreLogin)
// PostLogin - Welcome players after successful login
event.Subscribe(mgr, normalPriority, handlePostLogin)
// ServerPostConnect - Handle server connections
event.Subscribe(mgr, normalPriority, handleServerConnect)
// Disconnect - Track disconnections
event.Subscribe(mgr, normalPriority, handleDisconnect)
// Chat - Filter chat messages
event.Subscribe(mgr, highPriority, handleChat)
// KickedFromServer - Handle server kicks gracefully
event.Subscribe(mgr, normalPriority, handleServerKick(p))
// Ping - Customize server list ping response
event.Subscribe(mgr, normalPriority, handlePing)
}
// PreLoginEvent - Called before player authentication
func handlePreLogin(e *proxy.PreLoginEvent) {
username := e.Username()
// Example: Block certain usernames
bannedNames := []string{"hacker", "cheater", "admin"}
for _, banned := range bannedNames {
if strings.EqualFold(username, banned) {
e.Deny(&component.Text{
Content: "This username is not allowed on this server.",
S: component.Style{Color: color.Red},
})
return
}
}
// Example: Maintenance mode
maintenanceMode := false
if maintenanceMode {
e.Deny(&component.Text{
Content: "Server is currently in maintenance mode.",
S: component.Style{Color: color.Yellow},
})
return
}
// Allow the connection
e.Allow()
}
// PostLoginEvent - Called after successful login
func handlePostLogin(e *proxy.PostLoginEvent) {
player := e.Player()
// Send welcome message
_ = player.SendMessage(&component.Text{
S: component.Style{Color: color.Gold},
Extra: []component.Component{
&component.Text{
Content: "Welcome to the Network, ",
},
&component.Text{
Content: player.Username(),
S: component.Style{Color: color.Aqua, Bold: component.True},
},
&component.Text{
Content: "!",
},
},
})
// Log the connection
fmt.Printf("[+] %s logged in from %s\n", player.Username(), player.RemoteAddress())
}
// ServerPostConnectEvent - Called when player connects to a backend server
func handleServerConnect(e *proxy.ServerPostConnectEvent) {
player := e.Player()
currentServer := player.CurrentServer()
if currentServer == nil {
return
}
serverName := currentServer.Server().ServerInfo().Name()
previousServer := e.PreviousServer()
// First server connection
if previousServer == nil {
_ = player.SendMessage(&component.Text{
Content: fmt.Sprintf("Connected to %s", serverName),
S: component.Style{Color: color.Green},
})
fmt.Printf("[→] %s connected to %s\n", player.Username(), serverName)
} else {
// Server switch
_ = player.SendMessage(&component.Text{
Content: fmt.Sprintf("Switched to %s", serverName),
S: component.Style{Color: color.Aqua},
})
fmt.Printf("[→] %s switched from %s to %s\n",
player.Username(),
previousServer.ServerInfo().Name(),
serverName)
}
}
// DisconnectEvent - Called when player disconnects
func handleDisconnect(e *proxy.DisconnectEvent) {
player := e.Player()
status := e.LoginStatus()
statusStr := "unknown"
switch status {
case proxy.SuccessfulLoginStatus:
statusStr = "clean disconnect"
case proxy.ConflictingLoginStatus:
statusStr = "conflicting login"
case proxy.CanceledByUserLoginStatus:
statusStr = "canceled by user"
case proxy.CanceledByProxyLoginStatus:
statusStr = "canceled by proxy"
}
fmt.Printf("[-] %s disconnected (%s)\n", player.Username(), statusStr)
}
// PlayerChatEvent - Called when player sends chat message
func handleChat(e *proxy.PlayerChatEvent) {
player := e.Player()
message := e.Message()
// Example: Block messages containing certain words
blockedWords := []string{"spam", "badword"}
for _, word := range blockedWords {
if strings.Contains(strings.ToLower(message), word) {
e.SetAllowed(false)
_ = player.SendMessage(&component.Text{
Content: "Your message contains blocked words.",
S: component.Style{Color: color.Red},
})
return
}
}
// Example: Auto-replace certain phrases
if strings.Contains(message, "discord") {
newMessage := strings.ReplaceAll(message, "discord", "our community")
e.SetMessage(newMessage)
}
// Log chat messages
fmt.Printf("[CHAT] %s: %s\n", player.Username(), message)
}
// KickedFromServerEvent - Handle when player is kicked from backend server
func handleServerKick(p *proxy.Proxy) func(*proxy.KickedFromServerEvent) {
return func(e *proxy.KickedFromServerEvent) {
player := e.Player()
server := e.Server()
reason := e.OriginalReason()
fmt.Printf("[!] %s was kicked from %s\n", player.Username(), server.ServerInfo().Name())
// If kicked during server connect, try to send to fallback
if e.KickedDuringServerConnect() {
// Try to find a fallback server
fallbackServer := findFallbackServer(p, server)
if fallbackServer != nil {
e.SetResult(&proxy.RedirectPlayerKickResult{
Server: fallbackServer,
Message: &component.Text{
S: component.Style{Color: color.Yellow},
Extra: []component.Component{
&component.Text{
Content: "Could not connect to ",
},
&component.Text{
Content: server.ServerInfo().Name(),
S: component.Style{Color: color.Red},
},
&component.Text{
Content: ". Redirecting to ",
},
&component.Text{
Content: fallbackServer.ServerInfo().Name(),
S: component.Style{Color: color.Green},
},
},
},
})
return
}
} else {
// Player was kicked from their current server
// Just notify them but keep them connected
e.SetResult(&proxy.NotifyKickResult{
Message: &component.Text{
S: component.Style{Color: color.Red},
Extra: []component.Component{
&component.Text{
Content: "You were kicked: ",
},
reason,
},
},
})
return
}
// No fallback available, disconnect with reason
e.SetResult(&proxy.DisconnectPlayerKickResult{
Reason: reason,
})
}
}
// PingEvent - Customize server list response
func handlePing(e *proxy.PingEvent) {
ping := e.Ping()
// Customize MOTD
ping.Description = &component.Text{
Extra: []component.Component{
&component.Text{
Content: "Custom Gate Proxy",
S: component.Style{Color: color.Gold, Bold: component.True},
},
&component.Text{
Content: "\n",
},
&component.Text{
Content: "Powered by Go",
S: component.Style{Color: color.Aqua},
},
},
}
// Show one more slot than current players
ping.Players.Max = ping.Players.Online + 1
}
// Helper function to find a fallback server
func findFallbackServer(p *proxy.Proxy, avoid proxy.RegisteredServer) proxy.RegisteredServer {
for _, server := range p.Servers() {
if server.ServerInfo().Name() != avoid.ServerInfo().Name() {
return server
}
}
return nil
}