Skip to content

Sounds

Gate provides a dedicated sound package for playing and controlling sounds for Minecraft players. This API allows you to create immersive audio experiences in your proxy extensions.

Version Requirements

  • Minimum Minecraft Version: 1.19.3
  • UI Sound Source: Requires 1.21.5+

Players on older versions will receive an error if they try to use sound features.

Package Import

go
import (
    "go.minekube.com/gate/pkg/edition/java/sound"
)

Quick Start

Playing Sounds

The simplest way to play a sound:

go
// Create a sound
snd := sound.NewSound("entity.player.levelup", sound.SourcePlayer)

// Play it for the player (emitted from the player's location)
err := sound.Play(player, snd, player)

Stopping Sounds

go
// Stop all sounds
err := sound.StopAll(player)

// Stop sounds from a specific source
err := sound.StopSource(player, sound.SourceMusic)

// Stop a specific sound
err := sound.StopSound(player, "entity.cat.ambient")

Sound Configuration

The Sound struct allows you to customize playback:

go
snd := sound.NewSound("block.note_block.pling", sound.SourceBlock).
    WithVolume(2.0).  // Louder (also increases range)
    WithPitch(1.5).   // Higher pitch
    WithSeed(12345)   // Specific random seed

Sound Properties

  • Name: The sound identifier (e.g., "entity.experience_orb.pickup")
  • Source: The sound category/source (affects volume controls)
  • Volume: 0.0 to (values > 1.0 increase audible range)
  • Pitch: 0.5 to 2.0 (lower = deeper, higher = higher pitch)
  • Seed: Optional random seed for sound variation

Sound Sources

Sound sources determine which volume slider in the client controls the sound:

DescriptionConstant
Master volumesound.SourceMaster
Background musicsound.SourceMusic
Jukebox/Music discssound.SourceRecord
Rain, thundersound.SourceWeather
Block soundssound.SourceBlock
Hostile mobssound.SourceHostile
Neutral mobssound.SourceNeutral
Player actionssound.SourcePlayer
Ambient soundssound.SourceAmbient
Voice chatsound.SourceVoice
Interface soundssound.SourceUI ⚠️ 1.21.5+

Parsing Sound Sources

go
// From string
source, err := sound.ParseSource("music")
if err != nil {
    // Invalid source name
}

// To string
name := source.String() // "music"

Playing Sounds from Different Emitters

Sounds can be emitted from different players on the same server:

go
targetPlayer := ... // player who will hear the sound
emitterPlayer := ... // player from whose location the sound plays

snd := sound.NewSound("entity.villager.yes", sound.SourceNeutral)

// Play sound at emitter's location for target player
err := sound.Play(targetPlayer, snd, emitterPlayer)

Same Server Required

Both the target player and emitter must be connected to the same backend server. Otherwise, ErrDifferentServers will be returned.

Advanced Stop Options

The sound.Stop() function provides flexible filtering:

go
// Stop specific sound from specific source
source := sound.SourceAmbient
soundName := "entity.cat.ambient"
err := sound.Stop(player, &source, &soundName)

// Stop all sounds from a source (soundName = nil)
err := sound.Stop(player, &source, nil)

// Stop a sound from all sources (source = nil)
err := sound.Stop(player, nil, &soundName)

// Stop everything (both nil)
err := sound.Stop(player, nil, nil)

Complete Example: Event-Based Sounds

Play a sound when a player connects to a server:

go
package main

import (
	"context"

	"go.minekube.com/gate/pkg/edition/java/proxy"
	"go.minekube.com/gate/pkg/edition/java/sound"
	"go.minekube.com/gate/pkg/gate"
)

func main() {
	proxy.Plugins = append(proxy.Plugins, proxy.Plugin{
		Name: "SoundExample",
		Init: func(ctx context.Context, p *proxy.Proxy) error {
			return registerSoundExample(p)
		},
	})
	gate.Execute()
}

func registerSoundExample(p *proxy.Proxy) error {
	// Subscribe to server connected event
	p.Event().Subscribe(&proxy.ServerPostConnectEvent{}, 0, func(e *proxy.ServerPostConnectEvent) {
		player := e.Player()

		// Play a level-up sound when player connects to a server
		levelUpSound := sound.NewSound("entity.player.levelup", sound.SourcePlayer).
			WithVolume(1.0).
			WithPitch(1.0)

		_ = sound.Play(player, levelUpSound, player)
	})

	return nil
}

Complete Example: Sound Commands

Create commands to test the sound API:

go
package main

import (
	"context"
	"fmt"
	"strconv"

	"go.minekube.com/brigodier"
	. "go.minekube.com/common/minecraft/color"
	. "go.minekube.com/common/minecraft/component"
	"go.minekube.com/gate/pkg/command"
	"go.minekube.com/gate/pkg/edition/java/proxy"
	"go.minekube.com/gate/pkg/edition/java/sound"
	"go.minekube.com/gate/pkg/gate"
)

func main() {
	proxy.Plugins = append(proxy.Plugins, proxy.Plugin{
		Name: "SoundCommandExample",
		Init: func(ctx context.Context, p *proxy.Proxy) error {
			return registerSoundCommands(p)
		},
	})
	gate.Execute()
}

func registerSoundCommands(p *proxy.Proxy) error {
	// Register /playsound command
	p.Command().Register(
		brigodier.Literal("playsound").
			Then(brigodier.Argument("sound", brigodier.String).
				Then(brigodier.Argument("source", brigodier.String).
					Then(brigodier.Argument("volume", brigodier.String).
						Then(brigodier.Argument("pitch", brigodier.String).
							Executes(command.Command(func(c *command.Context) error {
								player, ok := c.Source.(proxy.Player)
								if !ok {
									return c.Source.SendMessage(&Text{
										S:       Style{Color: Red},
										Content: "Only players can use this command!",
									})
								}

								soundName := c.String("sound")
								source, err := sound.ParseSource(c.String("source"))
								if err != nil {
									return c.Source.SendMessage(&Text{
										S:       Style{Color: Red},
										Content: err.Error(),
									})
								}

								volume, err := strconv.ParseFloat(c.String("volume"), 32)
								if err != nil {
									return c.Source.SendMessage(&Text{
										S:       Style{Color: Red},
										Content: "Invalid volume",
									})
								}

								pitch, err := strconv.ParseFloat(c.String("pitch"), 32)
								if err != nil {
									return c.Source.SendMessage(&Text{
										S:       Style{Color: Red},
										Content: "Invalid pitch",
									})
								}

								// Create and play the sound
								snd := sound.NewSound(soundName, source).
									WithVolume(float32(volume)).
									WithPitch(float32(pitch))

								if err := sound.Play(player, snd, player); err != nil {
									return c.Source.SendMessage(&Text{
										S:       Style{Color: Red},
										Content: fmt.Sprintf("Error: %v", err),
									})
								}

								return c.Source.SendMessage(&Text{
									S: Style{Color: Green},
									Content: fmt.Sprintf("Playing %s at %.1f volume, %.1f pitch",
										soundName, volume, pitch),
								})
							})),
						),
					),
				),
			),
	)

	// Register /stopsound command
	p.Command().Register(
		brigodier.Literal("stopsound").
			Executes(command.Command(func(c *command.Context) error {
				player, ok := c.Source.(proxy.Player)
				if !ok {
					return c.Source.SendMessage(&Text{
						S:       Style{Color: Red},
						Content: "Only players can use this command!",
					})
				}

				if err := sound.StopAll(player); err != nil {
					return c.Source.SendMessage(&Text{
						S:       Style{Color: Red},
						Content: fmt.Sprintf("Error: %v", err),
					})
				}

				return c.Source.SendMessage(&Text{
					S:       Style{Color: Green},
					Content: "Stopped all sounds",
				})
			})),
	)

	return nil
}

Common Sound Names

UI & Feedback Sounds

  • entity.experience_orb.pickup - XP orb pickup sound
  • entity.player.levelup - Level up fanfare
  • ui.button.click - Button click
  • entity.villager.yes / entity.villager.no - Villager sounds

Note Block Sounds

  • block.note_block.bell - Bell
  • block.note_block.pling - Pling (high pitch)
  • block.note_block.harp - Harp
  • block.note_block.bass - Bass
  • block.note_block.guitar - Guitar

Entity Sounds

  • entity.enderman.teleport - Teleport effect
  • entity.arrow.hit_player - Arrow hit
  • entity.item.pickup - Item pickup

Find More Sounds

For a complete list of available sounds, see the Minecraft Wiki - Sound Events.

Error Handling

The sound API returns specific errors for better handling:

go
err := sound.Play(player, snd, emitter)
if err != nil {
    switch {
    case errors.Is(err, sound.ErrUnsupportedClientProtocol):
        // Player is on a version < 1.19.3
    case errors.Is(err, sound.ErrUISourceUnsupported):
        // Player tried to use UI source on version < 1.21.5
    case errors.Is(err, sound.ErrNotConnected):
        // Player is not connected to a server
    case errors.Is(err, sound.ErrDifferentServers):
        // Emitter and target are on different servers
    }
}

Known Minecraft Bugs

Minecraft Limitations

  • MC-146721: Stereo sounds are always played globally in 1.14+ (not positional)
  • MC-138832: Volume and pitch are ignored in Minecraft 1.14 to 1.16.5
  • Invalid sound names will silently fail (no error, but no sound plays)

API Reference

Functions

sound.Play(player, sound, emitter) error

Plays a sound at an entity's location.

Parameters:

  • player - The player who will hear the sound
  • sound - The sound configuration
  • emitter - The player from whose location the sound plays

Returns: Error if version incompatible or players not on same server

sound.Stop(player, source, soundName) error

Stops sounds based on criteria.

Parameters:

  • player - The player for whom to stop sounds
  • source - Sound source filter (nil = any)
  • soundName - Sound name filter (nil = any)

Returns: Error if version incompatible

Helper Functions

  • sound.StopAll(player) error - Stop all sounds
  • sound.StopSource(player, source) error - Stop all sounds from a source
  • sound.StopSound(player, name) error - Stop a specific sound

See Also

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