Skip to content

Events

Gate provides a powerful event system that allows you to listen to and modify events that occur in the proxy.

Subscribing to Events

Events are a way to communicate between different parts between your code and Gate. They are a way to decouple Gate from your own application code and make it more flexible.

Checkout the Simple Proxy for more examples.

Example:

go
package developers

import (
	"github.com/robinbraemer/event"
	"go.minekube.com/common/minecraft/component"
	"go.minekube.com/gate/pkg/edition/java/proxy"
)

func SubscribeExample(p *proxy.Proxy) {
	// Get the event manager.
	mgr := p.Event()

	// Subscribe to an event.
	const priority = 0
	event.Subscribe(mgr, priority, func(e *proxy.PreLoginEvent) {
		// Kicks every player
		e.Deny(&component.Text{Content: "Sorry, the server is in maintenance."})
	})
}

Available Events

Available Events

See source on GitHub.

go
package proxy

import (
	"net"

	"go.minekube.com/gate/pkg/edition/java/proxy/internal/resourcepack"
	"go.minekube.com/gate/pkg/util/uuid"

	"go.minekube.com/brigodier"
	"go.minekube.com/common/minecraft/component"

	"go.minekube.com/gate/pkg/command"
	"go.minekube.com/gate/pkg/edition/java/forge/modinfo"
	"go.minekube.com/gate/pkg/edition/java/ping"
	"go.minekube.com/gate/pkg/edition/java/profile"
	"go.minekube.com/gate/pkg/edition/java/proto/packet"
	"go.minekube.com/gate/pkg/edition/java/proxy/message"
	"go.minekube.com/gate/pkg/edition/java/proxy/player"
	"go.minekube.com/gate/pkg/util/permission"
)

// PingEvent is fired when a request for server information is sent by a remote client,
// or when the server sends the MOTD and favicon to the client after a successful login.
// The proxy will wait on this event to finish firing before delivering the results to
// the remote client, but you are urged to handle this event as quickly as possible when
// handling this event due to the amount of ping packets a client can send.
type PingEvent struct {
	inbound Inbound
	ping    *ping.ServerPing
}

// Connection returns the inbound connection.
func (p *PingEvent) Connection() Inbound {
	return p.inbound
}

// Ping returns the used ping. (pre-initialized by the proxy)
func (p *PingEvent) Ping() *ping.ServerPing {
	return p.ping
}

// SetPing sets the ping response to use.
func (p *PingEvent) SetPing(ping *ping.ServerPing) {
	p.ping = ping
}

//
//
//
//
//

// ConnectionEventConn tracks whether Close was called on the connection.
type ConnectionEventConn interface {
	net.Conn
	Closed() bool // Whether Close was called.
}

// ConnectionEvent is fired when a client connects with the proxy.
// It can be used for low-level connection handling, modifying or closing the connection.
// This event is fired before ConnectionHandshakeEvent
type ConnectionEvent struct {
	conn     net.Conn
	original ConnectionEventConn
}

// Connection returns the connection.
func (e *ConnectionEvent) Connection() net.Conn {
	return e.conn
}

// OriginalConnection returns the original connection.
func (e *ConnectionEvent) OriginalConnection() ConnectionEventConn {
	return e.original
}

// SetConnection sets new connection to use for this connection.
func (e *ConnectionEvent) SetConnection(conn net.Conn) {
	e.conn = conn
}

//
//
//
//
//

// ConnectionHandshakeEvent is fired when a handshake
// is established between a client and the proxy.
type ConnectionHandshakeEvent struct {
	inbound Inbound
	intent  packet.HandshakeIntent
}

// Connection returns the inbound connection.
func (e *ConnectionHandshakeEvent) Connection() Inbound {
	return e.inbound
}

// Intent returns the handshake intent.
func (e *ConnectionHandshakeEvent) Intent() packet.HandshakeIntent {
	return e.intent
}

//
//
//
//
//

// GameProfileRequestEvent is fired after the PreLoginEvent in
// order to set up the game profile for the user.
// This can be used to configure a custom profile for a user, i.e. skin replacement.
type GameProfileRequestEvent struct {
	inbound    Inbound
	original   profile.GameProfile
	onlineMode bool

	use profile.GameProfile
}

// NewGameProfileRequestEvent creates a new GameProfileRequestEvent.
func NewGameProfileRequestEvent(
	inbound Inbound,
	original profile.GameProfile,
	onlineMode bool,
) *GameProfileRequestEvent {
	return &GameProfileRequestEvent{
		inbound:    inbound,
		original:   original,
		onlineMode: onlineMode,
	}
}

// Conn returns the inbound connection that is connecting to the proxy.
func (e *GameProfileRequestEvent) Conn() Inbound {
	return e.inbound
}

// Original returns the by the proxy created offline or online (Mojang authenticated) game profile.
func (e *GameProfileRequestEvent) Original() profile.GameProfile {
	return e.original
}

// OnlineMode specifies whether the user connected in online/offline mode.
func (e *GameProfileRequestEvent) OnlineMode() bool {
	return e.onlineMode
}

// SetGameProfile sets the profile to use for this connection.
func (e *GameProfileRequestEvent) SetGameProfile(p profile.GameProfile) {
	e.use = p
}

// GameProfile returns the game profile that will be used to initialize the connection with.
// Should no profile be set, the original profile (given by the proxy) will be used.
func (e *GameProfileRequestEvent) GameProfile() profile.GameProfile {
	if len(e.use.Name) == 0 {
		return e.original
	}
	return e.use
}

//
//
//
//
//
//

// PlayerModInfoEvent is fired when a Forge client sends its
// mods to the proxy while connecting to a server.
type PlayerModInfoEvent struct {
	player  Player
	modInfo modinfo.ModInfo
}

// Player returns the player who sent the mod info.
func (e *PlayerModInfoEvent) Player() Player {
	return e.player
}

// ModInfo is the mod info received by the player.
func (e *PlayerModInfoEvent) ModInfo() modinfo.ModInfo {
	return e.modInfo
}

//
//
//
//
//
//
//
//

// PermissionsSetupEvent is fired once a permission.Subject's
// permissions are being initialized.
type PermissionsSetupEvent struct {
	subject     permission.Subject
	defaultFunc permission.Func

	fn permission.Func
}

// Subject returns the subject the permissions are setup for.
func (p *PermissionsSetupEvent) Subject() permission.Subject {
	return p.subject
}

// Func returns the permission.Func used for the subject.
func (p *PermissionsSetupEvent) Func() permission.Func {
	if p.fn == nil {
		return p.defaultFunc
	}
	return p.fn
}

// SetFunc sets the permission.Func usec for the subject.
// If fn is nil, the default Func fill be used.
func (p *PermissionsSetupEvent) SetFunc(fn permission.Func) {
	if fn == nil {
		return
	}
	p.fn = fn
}

//
//
//
//
//
//
//

// PreLoginEvent is fired when a player has initiated a connection with the proxy
// but before the proxy authenticates the player with Mojang or before the player's proxy connection
// is fully established (for offline mode).
type PreLoginEvent struct {
	connection Inbound
	username   string
	id         uuid.UUID // player's uuid, nil-able

	result PreLoginResult
	reason component.Component
}

func newPreLoginEvent(conn Inbound, username string, id uuid.UUID) *PreLoginEvent {
	return &PreLoginEvent{
		connection: conn,
		username:   username,
		id:         id,
		result:     AllowedPreLogin,
	}
}

// PreLoginResult is the result of a PreLoginEvent.
type PreLoginResult uint8

// PreLoginResult values.
const (
	AllowedPreLogin PreLoginResult = iota
	DeniedPreLogin
	ForceOnlineModePreLogin
	ForceOfflineModePreLogin
)

// Username returns the username of the player.
func (e *PreLoginEvent) Username() string {
	return e.username
}

// ID returns the UUID of the connecting player. May be uuid.Nil!
// This value is nil on 1.19.2 and lower,
// up to 1.20.1 it is optional and from 1.20.2 it will always be available.
func (e *PreLoginEvent) ID() (uuid.UUID, bool) {
	return e.id, e.id == uuid.Nil
}

// Conn returns the inbound connection that is connecting to the proxy.
func (e *PreLoginEvent) Conn() Inbound {
	return e.connection
}

// Result returns the current result of the PreLoginEvent.
func (e *PreLoginEvent) Result() PreLoginResult {
	return e.result
}

// Reason returns the `deny reason` to disconnect the connection.
// May be nil!
func (e *PreLoginEvent) Reason() component.Component {
	return e.reason
}

func (e *PreLoginEvent) Deny(reason component.Component) {
	e.result = DeniedPreLogin
	e.reason = reason
}

func (e *PreLoginEvent) Allow() {
	e.result = AllowedPreLogin
	e.reason = nil
}

func (e *PreLoginEvent) ForceOnlineMode() {
	e.result = ForceOnlineModePreLogin
	e.reason = nil
}

func (e *PreLoginEvent) ForceOfflineMode() {
	e.result = ForceOfflineModePreLogin
	e.reason = nil
}

//
//
//
//
//
//
//
//

type LoginEvent struct {
	player Player

	denied bool
	reason component.Component
}

func (e *LoginEvent) Player() Player {
	return e.player
}

func (e *LoginEvent) Deny(reason component.Component) {
	e.denied = true
	e.reason = reason
}

func (e *LoginEvent) Allow() {
	e.denied = false
	e.reason = nil
}

func (e *LoginEvent) Allowed() bool {
	return !e.denied
}

// Is nil if Allowed() returns true
func (e *LoginEvent) Reason() component.Component {
	return e.reason
}

//
//
//
//
//
//
//

type DisconnectEvent struct {
	player      Player
	loginStatus LoginStatus
}

type LoginStatus uint8

const (
	SuccessfulLoginStatus LoginStatus = iota
	ConflictingLoginStatus
	CanceledByUserLoginStatus
	CanceledByProxyLoginStatus
	CanceledByUserBeforeCompleteLoginStatus
)

func (e *DisconnectEvent) Player() Player {
	return e.player
}

func (e *DisconnectEvent) LoginStatus() LoginStatus {
	return e.loginStatus
}

//
//
//
//
//
//
//
//

type PostLoginEvent struct {
	player Player
}

func (e *PostLoginEvent) Player() Player {
	return e.player
}

//
//
//
//
//
//

// PlayerChooseInitialServerEvent is fired when a player has finished the login process,
// and we need to choose the first server to connect to.
// The proxy will wait on this event to finish firing before initiating the connection
// but you should try to limit the work done in this event.
// Failures will be handled by KickedFromServerEvent as normal.
type PlayerChooseInitialServerEvent struct {
	player        Player
	initialServer RegisteredServer // May be nil if no server is configured.
}

// Player returns the player to find the initial server for.
func (e *PlayerChooseInitialServerEvent) Player() Player {
	return e.player
}

// InitialServer returns the initial server or nil if no server is configured.
func (e *PlayerChooseInitialServerEvent) InitialServer() RegisteredServer {
	return e.initialServer
}

// SetInitialServer sets the initial server for the player.
func (e *PlayerChooseInitialServerEvent) SetInitialServer(server RegisteredServer) {
	e.initialServer = server
}

//
//
//
//
//
//

// ServerPreConnectEvent is fired before the player connects to a server.
type ServerPreConnectEvent struct {
	player   Player
	original RegisteredServer

	server RegisteredServer
}

func newServerPreConnectEvent(player Player, server RegisteredServer) *ServerPreConnectEvent {
	return &ServerPreConnectEvent{
		player:   player,
		original: server,
		server:   server,
	}
}

// Player returns the player that tries to connect to another server.
func (e *ServerPreConnectEvent) Player() Player {
	return e.player
}

// OriginalServer returns the server that the player originally tried to connect to.
// To get the server the player will connect to, see the Server() of this event.
// To get the server the player is currently on when this event is fired, use Player.getCurrentServer().
func (e *ServerPreConnectEvent) OriginalServer() RegisteredServer {
	return e.original
}

// Allow the player to connect to the specified server.
func (e *ServerPreConnectEvent) Allow(server RegisteredServer) {
	e.server = server
}

// Deny will cancel the player to connect to another server.
func (e *ServerPreConnectEvent) Deny() {
	e.server = nil
}

// Allowed returns true whether the connection is allowed.
func (e *ServerPreConnectEvent) Allowed() bool {
	return e.server != nil
}

// Server returns the server the player will connect to OR
// nil if Allowed() returns false.
func (e *ServerPreConnectEvent) Server() RegisteredServer {
	return e.server
}

//
//
//
//
//
//

// PreTransferEvent is fired before a player is transferred to another host,
// either by the backend server or by a plugin using the Player.TransferTo method.
type PreTransferEvent struct {
	player       Player
	originalAddr net.Addr
	targetAddr   net.Addr
	denied       bool
}

func newPreTransferEvent(player Player, addr net.Addr) *PreTransferEvent {
	return &PreTransferEvent{
		player:       player,
		originalAddr: addr,
		targetAddr:   addr,
	}
}

// TransferTo changes the target address the player will be transferred to.
func (e *PreTransferEvent) TransferTo(addr net.Addr) {
	e.targetAddr = addr
	e.denied = false
}

// Addr returns the target address the player will be transferred to.
func (e *PreTransferEvent) Addr() net.Addr {
	return e.targetAddr
}

// Allowed returns true if the transfer is allowed.
func (e *PreTransferEvent) Allowed() bool {
	return !e.denied
}

// Player returns the player that is about to be transferred.
func (e *PreTransferEvent) Player() Player {
	return e.player
}

//
//
//
//
//
//

// Fired when a player is kicked from a server. You may either allow the proxy to kick the player
// (with an optional reason override) or redirect the player to a separate server. By default,
// the proxy will notify the user (if they are already connected to a server) or disconnect them
// (if they are not on a server and no other servers are available).
type KickedFromServerEvent struct {
	player              Player
	server              RegisteredServer
	originalReason      component.Component // May be nil!
	duringServerConnect bool

	result ServerKickResult
}

// ServerKickResult is the result of a KickedFromServerEvent and is implemented by
//
// # DisconnectPlayerKickResult
//
// # RedirectPlayerKickResult
//
// NotifyKickResult
type ServerKickResult interface {
	isServerKickResult() // assert implemented internally
}

var (
	_ ServerKickResult = (*DisconnectPlayerKickResult)(nil)
	_ ServerKickResult = (*RedirectPlayerKickResult)(nil)
	_ ServerKickResult = (*NotifyKickResult)(nil)
)

func newKickedFromServerEvent(
	player Player, server RegisteredServer,
	reason component.Component, duringServerConnect bool,
	initialResult ServerKickResult,
) *KickedFromServerEvent {
	return &KickedFromServerEvent{
		player:              player,
		server:              server,
		originalReason:      reason,
		duringServerConnect: duringServerConnect,
		result:              initialResult,
	}
}

// Player returns the player that got kicked.
func (e *KickedFromServerEvent) Player() Player {
	return e.player
}

// Server returns the server the player got kicked from.
func (e *KickedFromServerEvent) Server() RegisteredServer {
	return e.server
}

// OriginalReason returns the reason the server kicked the player from the server.
// May return nil!
func (e *KickedFromServerEvent) OriginalReason() component.Component {
	return e.originalReason
}

// KickedDuringServerConnect returns true if the player got kicked while connecting to another server.
func (e *KickedFromServerEvent) KickedDuringServerConnect() bool {
	return e.duringServerConnect
}

// Result returns current kick result.
// The proxy sets a default non-nil result but an event handler
// may has set it nil when handling the event.
func (e *KickedFromServerEvent) Result() ServerKickResult {
	return e.result
}

// SetResult sets the kick result.
func (e *KickedFromServerEvent) SetResult(result ServerKickResult) {
	e.result = result
}

// DisconnectPlayerKickResult is a ServerKickResult and
// tells the proxy to disconnect the player with the specified reason.
type DisconnectPlayerKickResult struct {
	Reason component.Component
}

func (*DisconnectPlayerKickResult) isServerKickResult() {}

// RedirectPlayerKickResult is a ServerKickResult and
// tells the proxy to redirect the player to another server.
type RedirectPlayerKickResult struct {
	Server  RegisteredServer    // The new server to redirect the kicked player to.
	Message component.Component // Optional message sent to the player after redirecting.
}

func (*RedirectPlayerKickResult) isServerKickResult() {}

// NotifyKickResult is ServerKickResult and
// notifies the player with the specified message but does nothing else.
// This is only a valid result to use if the player was trying to connect
// to a different server, otherwise it is treated like a DisconnectPlayerKickResult result.
type NotifyKickResult struct {
	Message component.Component
}

func (*NotifyKickResult) isServerKickResult() {}

//
//
//
//
//
//

// ServerConnectedEvent is fired before the player completely transitions
// to the target server and the connection to the previous server has been
// de-established.
//
// Use Server to get the target server since Player.CurrentServer is yet nil or
// listen for ServerPostConnectEvent instead.
type ServerConnectedEvent struct {
	player         Player
	server         RegisteredServer
	previousServer RegisteredServer // nil-able
	entityID       int
}

// Player returns the associated player.
func (s *ServerConnectedEvent) Player() Player {
	return s.player
}

// Server returns the server the player connected to.
func (s *ServerConnectedEvent) Server() RegisteredServer {
	return s.server
}

// PreviousServer returns the server the player was previously connected to.
// May return nil if there was none!
func (s *ServerConnectedEvent) PreviousServer() RegisteredServer {
	return s.previousServer
}

// EntityID returns the current entity ID of the player.
// This is the entity ID the player has on the connected server and changes
// every time the player connects to a new server.
func (s *ServerConnectedEvent) EntityID() int {
	return s.entityID
}

//
//
//
//
//

// ServerPostConnectEvent is fired after the player has connected to a server.
// The server the player is now connected to is available in Player().CurrentServer().
type ServerPostConnectEvent struct {
	player         Player
	previousServer RegisteredServer // nil-able
}

func newServerPostConnectEvent(player Player, previousServer RegisteredServer) *ServerPostConnectEvent {
	return &ServerPostConnectEvent{player: player, previousServer: previousServer}
}

// Player returns the associated player.
func (s *ServerPostConnectEvent) Player() Player {
	return s.player
}

// PreviousServer returns the server the player was previously connected to.
// May return nil if there was none!
func (s *ServerPostConnectEvent) PreviousServer() RegisteredServer {
	return s.previousServer
}

//
//
//
//
//

// PluginMessageEvent is fired when a plugin message is sent to the proxy,
// either from a player or a server backend server.
type PluginMessageEvent struct {
	source     message.ChannelMessageSource
	target     message.ChannelMessageSink
	identifier message.ChannelIdentifier
	data       []byte

	forward bool
}

func (p *PluginMessageEvent) Source() message.ChannelMessageSource {
	return p.source
}
func (p *PluginMessageEvent) Target() message.ChannelMessageSink {
	return p.target
}
func (p *PluginMessageEvent) Identifier() message.ChannelIdentifier {
	return p.identifier
}
func (p *PluginMessageEvent) Data() []byte {
	return p.data
}
func (p *PluginMessageEvent) SetForward(forward bool) {
	p.forward = forward
}
func (p *PluginMessageEvent) Allowed() bool {
	return p.forward
}

//
//
//
//
//

type PlayerSettingsChangedEvent struct {
	player   Player
	settings player.Settings
}

// Player returns the player whose settings where updates/initialized.
func (s *PlayerSettingsChangedEvent) Player() Player {
	return s.player
}

// Settings returns player's new settings.
func (s *PlayerSettingsChangedEvent) Settings() player.Settings {
	return s.settings
}

//
//
//
//

// PlayerChatEvent is fired when a player sends a chat message.
// Note that messages with a leading "/" do not trigger this event, but instead CommandExecuteEvent.
type PlayerChatEvent struct {
	player   Player
	original string
	modified string

	denied bool
}

// Player returns the player that sent the message.
func (c *PlayerChatEvent) Player() Player {
	return c.player
}

// Message returns the message that will be sent by the player.
func (c *PlayerChatEvent) Message() string {
	if c.modified == "" {
		return c.original
	}
	return c.modified
}

// SetMessage modifies the message of the player.
func (c *PlayerChatEvent) SetMessage(msg string) {
	if msg == c.original {
		return // not modified
	}
	c.modified = msg
}

// Original returns the original message the player sent.
func (c *PlayerChatEvent) Original() string {
	return c.original
}

// SetAllowed sets whether the chat message is allowed.
// Deprecated: for 1.19.1 and newer, set this as denied will kick users.
func (c *PlayerChatEvent) SetAllowed(allowed bool) {
	c.denied = !allowed
}

// Allowed returns true when the chat message is allowed.
func (c *PlayerChatEvent) Allowed() bool {
	return !c.denied
}

//
//
//
//
//

// CommandExecuteEvent is fired when someone wants to execute a command.
type CommandExecuteEvent struct {
	source          command.Source
	commandline     string
	originalCommand string

	forward bool // forward command to server
	denied  bool
}

// Source returns the command source that wants to run the command.
func (c *CommandExecuteEvent) Source() command.Source {
	return c.source
}

// Command returns the whole commandline without the leading "/".
func (c *CommandExecuteEvent) Command() string {
	return c.commandline
}

// OriginalCommand returns the original command if SetCommand has changed it.
func (c *CommandExecuteEvent) OriginalCommand() string {
	return c.originalCommand
}

// SetCommand changes the command being executed without the leading "/".
func (c *CommandExecuteEvent) SetCommand(commandline string) {
	c.commandline = commandline
}

// SetAllowed sets whether the command is allowed to be executed.
func (c *CommandExecuteEvent) SetAllowed(allowed bool) {
	c.denied = !allowed
}

// Allowed returns true when the command is allowed to be executed.
func (c *CommandExecuteEvent) Allowed() bool {
	return !c.denied
}

// SetForward sets whether the command should be forwarded to the server.
func (c *CommandExecuteEvent) SetForward(forward bool) {
	c.forward = forward
}

// Forward returns true when the command should be forwarded to the server.
func (c *CommandExecuteEvent) Forward() bool {
	return c.forward
}

//
//
//
//

// TabCompleteEvent is fired after a tab complete response is sent by the remote server,
// for clients on1.12.2 and below. You have the opportunity to modify the response sent
// to the remote player.
type TabCompleteEvent struct {
	player         Player
	partialMessage string
	suggestions    []string
}

// Player returns the player requesting the tab completion.
func (t *TabCompleteEvent) Player() Player {
	return t.player
}

// Suggestions returns all the suggestions provided to the user, as a mutable list.
func (t *TabCompleteEvent) Suggestions() []string {
	return t.suggestions
}

// SetSuggestions sets the suggestions provided to the user.
func (t *TabCompleteEvent) SetSuggestions(s []string) {
	t.suggestions = s
}

// PartialMessage returns the message being partially completed.
func (t *TabCompleteEvent) PartialMessage() string {
	return t.partialMessage
}

//
//
//
//

// PlayerAvailableCommandsEvent allows plugins to modify the packet
// indicating commands available on the server to a Minecraft 1.13+ client.
type PlayerAvailableCommandsEvent struct {
	player   Player
	rootNode *brigodier.RootCommandNode
}

// Player returns the player that is about to see the available commands.
func (p *PlayerAvailableCommandsEvent) Player() Player {
	return p.player
}

// RootNode returns the available commands to the Player.
func (p *PlayerAvailableCommandsEvent) RootNode() *brigodier.RootCommandNode {
	return p.rootNode
}

//
//
//
//

// ResourcePackResponseStatus represents the possible statuses for the resource pack.
type ResourcePackResponseStatus = resourcepack.ResponseStatus

// Possible statuses for a resource pack.

const (
	// SuccessfulResourcePackResponseStatus indicates the resource pack was applied successfully.
	SuccessfulResourcePackResponseStatus ResourcePackResponseStatus = resourcepack.SuccessfulResponseStatus
	// DeclinedResourcePackResponseStatus indicates the player declined to download the resource pack.
	DeclinedResourcePackResponseStatus ResourcePackResponseStatus = resourcepack.DeclinedResponseStatus
	// FailedDownloadResourcePackResponseStatus indicates the player could not download the resource pack.
	FailedDownloadResourcePackResponseStatus ResourcePackResponseStatus = resourcepack.FailedDownloadResponseStatus
	// AcceptedResourcePackResponseStatus indicates the player has accepted the resource pack and is now downloading it.
	AcceptedResourcePackResponseStatus ResourcePackResponseStatus = resourcepack.AcceptedResponseStatus
	// DownloadedResourcePackResponseStatus indicates the player has downloaded the resource pack.
	DownloadedResourcePackResponseStatus ResourcePackResponseStatus = resourcepack.DownloadedResponseStatus
	// InvalidURLResourcePackResponseStatus indicates the URL of the resource pack failed to load.
	InvalidURLResourcePackResponseStatus ResourcePackResponseStatus = resourcepack.InvalidURLResponseStatus
	// FailedToReloadResourcePackResponseStatus indicates the player failed to reload the resource pack.
	FailedToReloadResourcePackResponseStatus ResourcePackResponseStatus = resourcepack.FailedToReloadResponseStatus
	// DiscardedResourcePackResponseStatus indicates the resource pack was discarded.
	DiscardedResourcePackResponseStatus ResourcePackResponseStatus = resourcepack.DiscardedResponseStatus
)

// PlayerResourcePackStatusEvent is fired when the status of a resource pack sent to the player by the server is
// changed. Depending on the result of this event (which the proxy will wait until completely fired),
// the player may be kicked from the server.
type PlayerResourcePackStatusEvent = resourcepack.PlayerResourcePackStatusEvent

//
//
//
//

// ServerResourcePackSendEvent is fired when the downstream server tries to send a player a ResourcePack packet.
// The proxy will wait on this event to finish before forwarding the resource pack to the user.
// If this event is denied, it will retroactively send a DENIED status to the downstream server in response.
// If the downstream server has it set to "forced" it will forcefully disconnect the user.
type ServerResourcePackSendEvent struct {
	denied               bool
	receivedResourcePack ResourcePackInfo
	providedResourcePack ResourcePackInfo
	serverConn           *serverConnection
}

// newServerResourcePackSendEvent creates a new ServerResourcePackSendEvent.
func newServerResourcePackSendEvent(
	packInfo ResourcePackInfo,
	serverConn *serverConnection,
) *ServerResourcePackSendEvent {
	return &ServerResourcePackSendEvent{
		receivedResourcePack: packInfo,
		providedResourcePack: packInfo,
		serverConn:           serverConn,
	}
}

// Allowed indicated whether sending the resource pack to the client is allowed.
func (e *ServerResourcePackSendEvent) Allowed() bool {
	return !e.denied
}

// SetAllowed allows or denies sending the resource pack to the client.
func (e *ServerResourcePackSendEvent) SetAllowed(allowed bool) {
	e.denied = !allowed
}

// ServerConnection returns the associated server connection.
func (e *ServerResourcePackSendEvent) ServerConnection() ServerConnection {
	return e.serverConn
}

// ReceivedResourcePack returns the resource pack send by the server.
func (e *ServerResourcePackSendEvent) ReceivedResourcePack() ResourcePackInfo {
	return e.receivedResourcePack
}

// ProvidedResourcePack returns the resource pack provided to the client if allowed.
func (e *ServerResourcePackSendEvent) ProvidedResourcePack() ResourcePackInfo {
	return e.providedResourcePack
}

// SetProvidedResourcePack sets the resource pack provided to the client if allowed.
func (e *ServerResourcePackSendEvent) SetProvidedResourcePack(pack ResourcePackInfo) {
	e.providedResourcePack = pack
}

//
//
//
//

// PlayerChannelRegisterEvent is fired when a client Player sends a plugin message through the
// register channel. The proxy will not wait on this event to finish firing.
type PlayerChannelRegisterEvent struct {
	channels []message.ChannelIdentifier
	player   Player
}

func (e *PlayerChannelRegisterEvent) Channels() []message.ChannelIdentifier {
	return e.channels
}

func (e *PlayerChannelRegisterEvent) Player() Player {
	return e.player
}

//
//
//
//

// ServerLoginPluginMessageEvent is fired when a server sends a login plugin message to the proxy.
// Plugins have the opportunity to respond to the messages as needed. The proxy will wait on this
// event to finish. The server will be responsible for continuing the login process once the server
// is satisfied with any login plugin responses sent by proxy plugins (or messages indicating a lack of response).
type ServerLoginPluginMessageEvent struct {
	id         message.ChannelIdentifier
	contents   []byte
	sequenceID int

	result ServerLoginPluginMessageResult
}

// Contents returns the contents of the login plugin message sent by the server.
func (e *ServerLoginPluginMessageEvent) Contents() []byte {
	return e.contents
}

// SequenceID returns the sequence id of the login plugin message sent by the server.
func (e *ServerLoginPluginMessageEvent) SequenceID() int {
	return e.sequenceID
}

func (e *ServerLoginPluginMessageEvent) Result() *ServerLoginPluginMessageResult {
	return &e.result
}

type ServerLoginPluginMessageResult struct {
	Response []byte
}

func (r *ServerLoginPluginMessageResult) Allowed() bool {
	return r.Response != nil
}

func (r *ServerLoginPluginMessageResult) Copy() []byte {
	res := make([]byte, len(r.Response))
	copy(res, r.Response)
	return res
}

func (r *ServerLoginPluginMessageResult) Reply(response []byte) *ServerLoginPluginMessageResult {
	return &ServerLoginPluginMessageResult{
		Response: response,
	}
}

//
//
//
//

// PlayerClientBrandEvent is fired when a Player sends the `minecraft:brand` plugin message.
// The proxy will not wait on event handlers to finish firing.
type PlayerClientBrandEvent struct {
	player Player
	brand  string
}

func (e *PlayerClientBrandEvent) Player() Player {
	return e.player
}
func (e *PlayerClientBrandEvent) Brand() string {
	return e.brand
}

//
//
//
//

// PreShutdownEvent is fired before the proxy begins to shut down by
// stopping to accept new connections and disconnect all players.
type PreShutdownEvent struct {
	reason component.Component // may be nil
}

// Reason returns the shutdown reason used to disconnect players with.
// May be nil!
func (s *PreShutdownEvent) Reason() component.Component {
	return s.reason
}

// SetReason sets the shutdown reason used to disconnect players with.
func (s *PreShutdownEvent) SetReason(reason component.Component) {
	s.reason = reason
}

//
//
//
//

// ReadyEvent is fired once the proxy was successfully
// initialized and is ready to serve connections.
//
// May be triggered multiple times on config reloads.
type ReadyEvent struct {
	addr string // The address the proxy is listening on.
}

// Addr returns the address the proxy is listening on.
func (r *ReadyEvent) Addr() string { return r.addr }

// ShutdownEvent is fired by the proxy after the proxy
// has stopped accepting connections and PreShutdownEvent,
// but before the proxy process exits.
//
// Subscribe to this event to gracefully stop any subtasks,
// such as plugin dependencies.
type ShutdownEvent struct{}

Released under the Apache 2.0 License. (web version: ffedb830)