Skip to content

Events & Hooks

Samuel De Oliveira edited this page Sep 30, 2026 · 3 revisions

Events & Hooks Reference

DreamVoice provides an event system across both Paper / Purpur and Fabric.

  • On Paper, all events extend ToolsEvent (informational) and ToolsCancelEvent (cancellable) and are dispatched via Bukkit's event bus.
  • On Fabric, all events extend VoiceEvent and are dispatched via the DreamVoiceEvents.VOICE_EVENT Fabric callback hook.

This allows plugins and mods to monitor, restrict, or modify voice behaviors in real time.


Event Catalog Reference

1. 3D Spatial Speakers (fr.dreamin.dreamvoice.api.speaker.event)

Event Class Cancellable Trigger Point Description / Parameters
SpeakerRegisterEvent Yes Before speaker is registered Fired when a speaker is registered. getSpeaker().
SpeakerUnregisterEvent No When speaker is removed Fired when a speaker is deleted. getSpeaker().
SpeakerLinkPlayerEvent Yes When player is linked Fired when a player is authorized to broadcast speech. getSpeaker(), getPlayerUuid().
SpeakerUnlinkPlayerEvent No When player is unlinked Fired when speech authorization is revoked. getSpeaker(), getPlayerUuid().
SpeakerPlaySoundEvent Yes Before audio starts Fired when playing an audio file, URL, or recording. getSpeaker(), getSource(), isLoop().
SpeakerStopSoundEvent No When audio is stopped Fired when audio playback is stopped. getSpeaker().

2. Wiretaps & Surveillance (fr.dreamin.dreamvoice.api.wiretap.event)

Event Class Cancellable Trigger Point Description / Parameters
WiretapRegisterEvent Yes Before wiretap is registered Fired when a listening bug is created. getWiretap().
WiretapRemoveEvent No When wiretap is removed Fired when a listening bug is deleted. getWiretap().
WiretapSubscribeEvent Yes When player listens Fired when a player starts eavesdropping (/wiretap listen). getWiretap(), getPlayerUuid().
WiretapUnsubscribeEvent No When player stops listening Fired when a player stops eavesdropping. getWiretap(), getPlayerUuid().

3. Radios & Transmitters (fr.dreamin.dreamvoice.api.radio.event, transmitter.event)

Event Class Cancellable Trigger Point Description / Parameters
RadioChannelJoinEvent Yes Before tuning into radio Fired when a player joins a frequency. getChannel(), getPlayerUuid().
RadioChannelLeaveEvent No When leaving frequency Fired when a player disconnects from radio. getChannel(), getPlayerUuid().
RadioChannelCreateEvent Yes Before new channel is created Fired when a new frequency channel is allocated. getChannel().
TransmitterToggleEvent Yes When toggling transmitter Fired when transmitter mode is activated or deactivated. getPlayerUuid(), isEnabled().

4. Audio Recordings & Cassettes (fr.dreamin.dreamvoice.api.recording.event)

Event Class Cancellable Trigger Point Description / Parameters
VoiceRecordingStartEvent Yes Before recording starts Fired when voice capture begins. getSpeakerUuid().
VoiceRecordingStopEvent No When recording ends Fired when recording finishes and .dv is saved. getRecording().
CassetteCreateEvent Yes When creating cassette item Fired when generating an interactive item. getRecording(), getItemStack(), setItemStack(...).
CassettePlayEvent Yes When player uses cassette Fired when playing tape in personal headphones. getPlayer(), getRecording(), getItemStack().

5. DSP Voice Filters (fr.dreamin.dreamvoice.api.filter.event)

Event Class Cancellable Trigger Point Description / Parameters
VoiceFilterApplyEvent Yes Before filter is applied Fired when attaching a filter (e.g. disguise). getPlayerUuid(), getFilter().
VoiceFilterRemoveEvent No When filter is removed Fired when detaching a filter. getPlayerUuid(), getFilter().

6. VoiceWall Acoustic Occlusion (fr.dreamin.dreamvoice.api.wall.event)

Event Class Cancellable Trigger Point Description / Parameters
VoiceWallOcclusionEvent Yes During acoustic raycast Asynchronous event fired when calculating wall occlusion. Canceling bypasses occlusion (0 dB). getSender(), getReceiver(), getLossDb(), setLossDb(double), isBlocked(), setBlocked(boolean).

7. Speech-to-Text & Keyword Spotting (fr.dreamin.dreamvoice.api.speech.event)

Event Class Cancellable Trigger Point Description / Parameters
VoiceKeywordSpokenEvent No Realtime keyword detected Fired when a player speaks a registered keyword matching Vosk grammar. getPlayer(), getKeyword(), getMatchedWord(), getConfidence().
TranscriptionStationStartEvent Yes Player interacts with station Fired when an audio cassette is placed on a transcription block. getPlayer(), getBlock(), getRecording().
TranscriptionCompleteEvent No Transcription finished Fired when speech-to-text finishes (local or sidecar). getRecordingId(), getResult().

8. Player & Voice Lifecycle (fr.dreamin.dreamvoice.api.player.event, voice.event)

Event Class Cancellable Trigger Point Description / Parameters
PlayerJoinVoiceEvent No Player connects to SVC Fired when wrapped into a VPlayer. getVPlayer().
PlayerLeaveVoiceEvent No Player disconnects from SVC Fired on voice disconnect. getVPlayer().
PlayerStateChangeEvent Yes Voice state change Fired when mute/deafen/whisper changes. getOldState(), getNewState().
MicrophonePacketEvent Yes Raw microphone packet received Asynchronous event fired per 20ms Opus frame from client.
EntitySoundPacketEvent Yes Positional 3D sound dispatched Asynchronous event dispatched before playing sound relative to an entity.

Fabric Event Registration

On Fabric, register a listener with DreamVoiceEvents.VOICE_EVENT:

import fr.dreamin.dreamvoice.fabric.event.DreamVoiceEvents;
import fr.dreamin.dreamvoice.api.speaker.event.SpeakerLinkPlayerEvent;
import fr.dreamin.dreamvoice.api.speech.event.VoiceKeywordSpokenEvent;
import net.fabricmc.api.ModInitializer;

public class MyFabricVoiceMod implements ModInitializer {

    @Override
    public void onInitialize() {
        DreamVoiceEvents.VOICE_EVENT.register(event -> {
            // Pattern matching on event types:
            if (event instanceof SpeakerLinkPlayerEvent speakerEvent) {
                if (speakerEvent.getSpeaker().getName().startsWith("VIP_")) {
                    speakerEvent.setCancelled(true);
                }
            } else if (event instanceof VoiceKeywordSpokenEvent keywordEvent) {
                System.out.println("Keyword detected: " + keywordEvent.getKeyword().getId());
            }
        });
    }
}

Paper Practical Implementation Examples

Example 1: Restricting Speakers to Host / Staff

Cancel unauthorized players attempting to link to public auditorium speakers:

import fr.dreamin.dreamvoice.api.speaker.event.SpeakerLinkPlayerEvent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public class SpeakerSecurityListener implements Listener {

    @EventHandler
    public void onSpeakerLink(SpeakerLinkPlayerEvent event) {
        Player player = Bukkit.getPlayer(event.getPlayerUuid());
        if (player == null) return;

        // If the speaker name starts with "Stage_", require staff permission:
        if (event.getSpeaker().getName().startsWith("Stage_") && !player.hasPermission("event.host")) {
            event.setCancelled(true);
            player.sendMessage("§cYou do not have permission to link to the stage speakers!");
        }
    }
}

Example 2: Requiring Walkie-Talkie Item for Radio Channels

Prevent players from joining a radio frequency unless they hold a physical Radio item:

import fr.dreamin.dreamvoice.api.radio.event.RadioChannelJoinEvent;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public class RadioInventoryListener implements Listener {

    @EventHandler
    public void onRadioJoin(RadioChannelJoinEvent event) {
        Player player = Bukkit.getPlayer(event.getPlayerUuid());
        if (player == null) return;

        // Check if player has an iron nugget named "Walkie-Talkie" in inventory:
        boolean hasRadio = player.getInventory().contains(Material.IRON_NUGGET);
        if (!hasRadio && !player.isOp()) {
            event.setCancelled(true);
            player.sendMessage("§cYou need a Walkie-Talkie device in your inventory to tune in!");
        }
    }
}

Example 3: Modifying Cassette Item Metadata

Customize the display name, custom model data, and lore of physical Cassette items when created:

import fr.dreamin.dreamvoice.api.recording.event.CassetteCreateEvent;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;

public class CassetteLoreListener implements Listener {

    @EventHandler
    public void onCassetteCreate(CassetteCreateEvent event) {
        ItemStack item = event.getItemStack();
        ItemMeta meta = item.getItemMeta();
        if (meta == null) return;

        // Add custom evidence tag for investigation minigame:
        meta.displayName(Component.text("Classified Tape", NamedTextColor.RED));
        meta.setCustomModelData(105);
        item.setItemMeta(meta);

        // Replace modified stack back into event:
        event.setItemStack(item);
    }
}

Example 4: Telepathic Link Through Walls (Bypassing Occlusion)

Allow a psychic role or ghost to speak through solid soundproof walls by canceling VoiceWallOcclusionEvent:

import fr.dreamin.dreamvoice.api.wall.event.VoiceWallOcclusionEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;

public class PsychicAcousticListener implements Listener {

    @EventHandler
    public void onOcclusion(VoiceWallOcclusionEvent event) {
        // If the sender is tagged with "ghost_whisper" metadata, completely bypass wall loss:
        if (event.getSender().hasTag("ghost_whisper")) {
            // Canceling the event sets acoustic attenuation to 0 dB (direct line of sight):
            event.setCancelled(true);
        } else if (event.getSender().hasTag("megaphone")) {
            // Cut wall dampening in half:
            event.setLossDb(event.getLossDb() * 0.5);
            event.setBlocked(false);
        }
    }
}

Clone this wiki locally