### Server Properties Example Source: https://plasmovoice.com/docs/server/advanced An example of a typical Minecraft server properties file, showing the server port. ```properties server-port=25565 ``` -------------------------------- ### Use Custom Javascript Placeholder Source: https://plasmovoice.com/docs/server/papi Example of how to use the custom placeholder created from the JavaScript script in-game. ```plaintext %javascript_check_voice% ``` -------------------------------- ### Download and Reload Javascript Extension Source: https://plasmovoice.com/docs/server/papi Installs the Javascript addon for PlaceholderAPI and reloads the server to apply changes. ```bash /papi ecloud download Javascript /papi reload ``` -------------------------------- ### Activation Listeners - Java Source: https://plasmovoice.com/docs/api/activations.html Implement listeners for player activation events: when a player starts speaking, is speaking, or finishes speaking. Return 'HANDLED' to cancel the associated PlayerSpeakEvent. ```java ServerActivation activation = /* */; activation.onPlayerActivationStart(player -> { System.out.println(player + " start speaking to activation"); }); activation.onPlayerActivation((player, packet) -> { System.out.println(player + " is speaking to activation"); // you need to return "HANDLED" if activation was handled successfully. // this will cancel PlayerSpeakEvent return ServerActivation.Result.HANDLED; }); activation.onPlayerActivationEnd((player, packet) -> { System.out.println(player + " has finished speaking to activation"); // you need to return "HANDLED" if activation was handled successfully. // this will cancel PlayerSpeakEndEvent return ServerActivation.Result.HANDLED; }); ``` -------------------------------- ### Override Activation Distance with DistanceSupplier (Java) Source: https://plasmovoice.com/docs/api/activations.html Implement `DistanceSupplier` to provide custom activation distances. This example sets a fixed distance of 12 units for all players. ```java package com.plasmovoice.testaddon; import su.plo.voice.api.addon.AddonInitializer; import su.plo.voice.api.addon.InjectPlasmoVoice; import su.plo.voice.api.addon.annotation.Addon; import su.plo.voice.api.server.PlasmoVoiceServer; import su.plo.voice.api.server.audio.capture.ProximityServerActivationHelper; import su.plo.voice.api.server.audio.capture.ServerActivation; import su.plo.voice.api.server.audio.line.ServerSourceLine; import su.plo.voice.api.server.player.VoiceServerPlayer; import su.plo.voice.proto.packets.tcp.serverbound.PlayerAudioEndPacket; import su.plo.voice.proto.packets.udp.serverbound.PlayerAudioPacket; @Addon( id = "pv-addon-test", name = "Test Addon", version = "1.0.0", authors = {"Plasmo"} ) public final class TestAddon implements AddonInitializer { @InjectPlasmoVoice private PlasmoVoiceServer voiceServer; private ProximityServerActivationHelper proximityHelper; @Override public void onAddonInitialize() { ServerActivation activation = /* register or get activation */; ServerSourceLine sourceLine = /* register or get source line */; this.proximityHelper = new ProximityServerActivationHelper( voiceServer, activation, sourceLine, new TestDistanceSupplier() ); proximityHelper.registerListeners(this); } @Override public void onAddonShutdown() { if (proximityHelper != null) { proximityHelper.unregisterListeners(this); } } private static class TestDistanceSupplier implements ProximityServerActivationHelper.DistanceSupplier { @Override public short getDistance(VoiceServerPlayer player, PlayerAudioEndPacket packet) { return 12; } @Override public short getDistance(VoiceServerPlayer player, PlayerAudioPacket packet) { return 12; } } } ``` -------------------------------- ### Activation Listeners - Kotlin Source: https://plasmovoice.com/docs/api/activations.html Implement listeners for player activation events: when a player starts speaking, is speaking, or finishes speaking. Return 'HANDLED' to cancel the associated PlayerSpeakEvent. ```kotlin val activation: ServerActivation = /* */; activation.onPlayerActivationStart { player -> println("$player start speaking to activation") } activation.onPlayerActivation { player, packet -> println("$player is speaking to activation") ServerActivation.Result.HANDLED } activation.onPlayerActivationEnd { player, packet -> println("$player has finished speaking to activation") ServerActivation.Result.HANDLED } ``` -------------------------------- ### Registering an activation Source: https://plasmovoice.com/docs/api/activations.html Demonstrates how to create and build a new server activation with various configuration options. ```APIDOC ## Registering an activation Activations are dynamically synced with players, allowing you to register or unregister activations at any time. The sync also operates with permissions. Consequently, when a player loses or gains their activation permission, the activation will be removed or added to the client, respectively. ### Java Example ```java PlasmoVoiceServer voiceServer = /* */; Object addon = /* */; ServerActivation activation = voiceServer.getActivationManager().createBuilder( addon, "priority", // name "pv.activation.priority", // translation key "plasmovoice:textures/icons/microphone_priority.png", // icon resource location "pv.activation.priority", // permission 10 // weight ).build(); ``` ### Kotlin Example ```kotlin val voiceServer: PlasmoVoiceServer val addon: Any val activation = voiceServer.activationManager.createBuilder( addon, "priority", // name "pv.activation.priority", // translation key "plasmovoice:textures/icons/microphone_priority.png", // icon resource location "pv.activation.priority", // permission 10 // weight ).build() ``` ### Using InputStream as Icon #### Java Example ```java PlasmoVoiceServer voiceServer = /* */; Object addon = /* */; try (InputStream is = Files.newInputStream(new File("icon.png").toPath())) { ServerActivation activation = voiceServer.getActivationManager().createBuilder( addon, "priority", // name "pv.activation.priority", // translation key is, // icon "pv.activation.priority", // permission 10 // weight ).build(); } catch (IOException e) { e.printStackTrace(); } ``` #### Kotlin Example ```kotlin val voiceServer: PlasmoVoiceServer val addon: Any val activation = Files.newInputStream(File("icon.png").toPath()).use { voiceServer.activationManager.createBuilder( this, "priority", // name "pv.activation.priority", // translation key it, // icon "pv.activation.priority", // permission 10 // weight ).build() } ``` ### Weight Activation weight is determined how high it will be in the list. Lower weight means higher place, if activations have the same weight, then they would be sorted alphabetically. ### Distances You can set a list of available distances using `ServerActivation.Builder#setDistances`. An empty list indicates that the activation doesn't have player-controlled distances. To set a dynamic range of distances, set the first element to `-1`: `(ImmutableList.of(-1, 64))`. This will define a range from 1 to 64. ### Other options See `ServerActivation.Builder` for all available builder options. ``` ```APIDOC ## Obtaining an existing activation If you don't want to create your own activation, you can obtain an existing one: ### Java Example ```java PlasmoVoiceServer voiceServer = /* */; ServerActivation activation = voiceServer.getActivationManager() .getActivationByName("proximity") .orElseThrow(() -> new IllegalStateException("Proximity activation not found")); ``` ### Kotlin Example ```kotlin val voiceServer: PlasmoVoiceServer val activation = voiceServer.activationManager .getActivationByName("proximity") .orElseThrow { IllegalStateException("Proximity activation not found") } ``` ``` ```APIDOC ## Activation listeners There are three types of listeners: * `onPlayerActivationStart` — invoked when a player starts to speak. * `onPlayerActivation` — invoked when a player is speaking. * `onPlayerActivationEnd` — invoked when a player has finished speaking. ### Java Example ```java ServerActivation activation = /* */; activation.onPlayerActivationStart(player -> { System.out.println(player + " start speaking to activation"); }); activation.onPlayerActivation((player, packet) -> { System.out.println(player + " is speaking to activation"); // you need to return "HANDLED" if activation was handled successfully. // this will cancel PlayerSpeakEvent return ServerActivation.Result.HANDLED; }); activation.onPlayerActivationEnd((player, packet) -> { System.out.println(player + " has finished speaking to activation"); // you need to return "HANDLED" if activation was handled successfully. // this will cancel PlayerSpeakEndEvent return ServerActivation.Result.HANDLED; }); ``` ### Kotlin Example ```kotlin val activation: ServerActivation = /* */; activation.onPlayerActivationStart { player -> println("$player start speaking to activation") } activation.onPlayerActivation { player, packet -> println("$player is speaking to activation") ServerActivation.Result.HANDLED } activation.onPlayerActivationEnd { player, packet -> println("$player has finished speaking to activation") ServerActivation.Result.HANDLED } ``` ``` -------------------------------- ### Load Spectator Addon (Java) Source: https://plasmovoice.com/docs/server/minestom Load the SpectatorAddon after the voice server has been initialized. This makes the addon's functionality available. ```java import su.plo.voice.spectator.SpectatorAddon; // ... on server startup voiceServer.onInitialize(); // ... after voice server initialization PlasmoVoiceServer.getAddonsLoader().load(new SpectatorAddon()); ``` -------------------------------- ### Override Activation Distance with DistanceSupplier (Kotlin) Source: https://plasmovoice.com/docs/api/activations.html Implement `DistanceSupplier` in Kotlin to provide custom activation distances. This example sets a fixed distance of 12 units for all players. ```kotlin package com.plasmovoice.testaddon import su.plo.voice.api.addon.AddonInitializer import su.plo.voice.api.addon.annotation.Addon import su.plo.voice.api.addon.injectPlasmoVoice import su.plo.voice.api.server.PlasmoVoiceServer import su.plo.voice.api.server.audio.capture.ProximityServerActivationHelper import su.plo.voice.api.server.audio.capture.ProximityServerActivationHelper.DistanceSupplier import su.plo.voice.api.server.audio.capture.ServerActivation import su.plo.voice.api.server.audio.line.ServerSourceLine import su.plo.voice.api.server.player.VoiceServerPlayer import su.plo.voice.proto.packets.tcp.serverbound.PlayerAudioEndPacket import su.plo.voice.proto.packets.udp.serverbound.PlayerAudioPacket @Addon( id = "pv-addon-test", name = "Test Addon", version = "1.0.0", authors = ["Plasmo"] ) class TestAddon : AddonInitializer { private val voiceServer: PlasmoVoiceServer by injectPlasmoVoice() private var proximityHelper: ProximityServerActivationHelper? = null override fun onAddonInitialize() { val activation: ServerActivation /* register or get activation */ val sourceLine: ServerSourceLine /* register or get source line */ proximityHelper = ProximityServerActivationHelper( voiceServer, activation, sourceLine, TestDistanceSupplier() ) proximityHelper!!.registerListeners(this) } override fun onAddonShutdown() { if (proximityHelper != null) { proximityHelper!!.unregisterListeners(this) } } private class TestDistanceSupplier : DistanceSupplier { override fun getDistance(player: VoiceServerPlayer, packet: PlayerAudioEndPacket): Short { return 12 } override fun getDistance(player: VoiceServerPlayer, packet: PlayerAudioPacket): Short { return 12 } } } ``` -------------------------------- ### Initialize Minestom Voice Server (Kotlin) Source: https://plasmovoice.com/docs/server/minestom Initialize the Minestom voice server on server startup and shut it down on server shutdown. A configuration directory will be created. ```kotlin // ... on server startup val voiceConfigDirectory = File("plasmovoice") val voiceServer = MinestomVoiceServer(voiceConfigDirectory) voiceServer.onInitialize() // ... on server shutdown voiceServer.onShutdown() ``` -------------------------------- ### Registering a source line Source: https://plasmovoice.com/docs/api/source-lines.html Demonstrates how to register a new source line with custom settings like name, icon, and weight. ```APIDOC ## Registering a source line ### Description Registers a new source line with the Plasmo Voice system. This involves creating a builder, configuring its properties, and then building the source line instance. ### Method `voiceServer.getSourceLineManager().createBuilder(...).build()` ### Parameters #### `createBuilder` Parameters - **addon** (Object) - The addon instance associated with this source line. - **name** (String) - The unique name for the source line. - **translation key** (String) - The translation key for the source line's display name. - **icon resource location** (String) - The resource location for the source line's icon. - **weight** (Integer) - The weight of the source line, determining its position in lists (lower is higher). #### `withPlayers` (Optional) - **enable** (Boolean) - If true, enables player synchronization features for this source line. ### Request Example (Java) ```java PlasmoVoiceServer voiceServer = /* */; Object addon = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager().createBuilder( addon, "priority", // name "pv.activation.priority", // translation key "plasmovoice:textures/icons/speaker_priority.png", // icon resource location 10 // weight ).build(); ``` ### Request Example (Kotlin) ```kotlin val voiceServer: PlasmoVoiceServer val addon: Any val sourceLine = voiceServer.sourceLineManager.createBuilder( addon, "priority", // name "pv.activation.priority", // translation key "plasmovoice:textures/icons/speaker_priority.png", // icon resource location 10 // weight ).build() ``` ### Features - **Icon**: Can be set using `InputStream`. - **Weight**: Determines the order in the list; lower weight means higher priority. - **Players in overlay**: Enable using `.withPlayers(true)` to manage player synchronization. ``` -------------------------------- ### Load Spectator Addon (Kotlin) Source: https://plasmovoice.com/docs/server/minestom Load the SpectatorAddon after the voice server has been initialized. This makes the addon's functionality available. ```kotlin import su.plo.voice.spectator.SpectatorAddon // ... on server startup voiceServer.onInitialize() // ... after voice server initialization PlasmoVoiceServer.getAddonsLoader().load(SpectatorAddon()) ``` -------------------------------- ### Initialize Proximity Server Activation Helper (Kotlin) Source: https://plasmovoice.com/docs/api/activations.html Initializes the ProximityServerActivationHelper by providing the PlasmoVoiceServer instance, an activation, and a source line. Listeners are registered upon initialization and unregistered upon shutdown. ```kotlin package com.plasmovoice.testaddon import su.plo.voice.api.addon.AddonInitializer import su.plo.voice.api.addon.annotation.Addon import su.plo.voice.api.addon.injectPlasmoVoice import su.plo.voice.api.server.PlasmoVoiceServer import su.plo.voice.api.server.audio.capture.ProximityServerActivationHelper import su.plo.voice.api.server.audio.capture.ServerActivation import su.plo.voice.api.server.audio.line.ServerSourceLine @Addon( id = "pv-addon-test", name = "Test Addon", version = "1.0.0", authors = ["Plasmo"] ) class TestAddon : AddonInitializer { private val voiceServer: PlasmoVoiceServer by injectPlasmoVoice() private var proximityHelper: ProximityServerActivationHelper? = null override fun onAddonInitialize() { val activation: ServerActivation = /* register or get activation */ val sourceLine: ServerSourceLine = /* register or get source line */ proximityHelper = ProximityServerActivationHelper(voiceServer, activation, sourceLine) .also { it.registerListeners(this) } } override fun onAddonShutdown() { proximityHelper?.unregisterListeners(this) } } ``` -------------------------------- ### Create Custom Voice Chat Status Script Source: https://plasmovoice.com/docs/server/papi A JavaScript script to check if a player has Plasmo Voice installed and return a status indicator. This script should be saved in the `javascripts/` directory. ```javascript var placeholder = "%plasmovoice_installed%"; var voice = PlaceholderAPI.static.setPlaceholders(BukkitPlayer, placeholder); var checkVoice = function (voice) { return voice === "true" ? " &a●&f | " : " &c●&f | "; }; checkVoice(voice); ``` -------------------------------- ### Handle Player Activation with Source Info (Java) Source: https://plasmovoice.com/docs/api/sources.html Sends audio frames with PlayerActivationInfo to a source during player activation. This allows addons to identify the destination source for the current activation. ```java ServerActivation activation = /* ... */; ServerProximitySource source = /* ... */; activation.onPlayerActivation((player, packet) -> { source.sendAudioFrame( packet.getData(), packet.getSequenceNumber(), packet.getDistance(), new PlayerActivationInfo(player, packet) ); return ServerActivation.Result.HANDLED; }); ``` -------------------------------- ### Broadcast Source Creation Source: https://plasmovoice.com/docs/api/sources.html Creates a broadcast audio source that can be heard by a group of players. If no group is specified, all players with Plasmo Voice installed will hear the audio. The group of players can be set using ServerBroadcastSource#setPlayers. ```APIDOC ## Broadcast Source Creation ### Description Creates a broadcast audio source that can be heard by a group of players. If no group is specified, all players with Plasmo Voice installed will hear the audio. The group of players can be set using ServerBroadcastSource#setPlayers. ### Java Example ```java PlasmoVoiceServer voiceServer = /* Initialize PlasmoVoiceServer */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager() .getLineByName("proximity") .orElseThrow(() -> new IllegalStateException("Proximity source line not found")); ServerBroadcastSource source = sourceLine.createBroadcastSource(false); // Set "listeners" for broadcast source VoicePlayer player = voiceServer.getPlayerManager() .getPlayerByName("Apehum") .orElseThrow(() -> new IllegalStateException("Player not found")); Set players = new HashSet<>(); players.add(player); source.setPlayers(players); ``` ### Kotlin Example ```kotlin val voiceServer: PlasmoVoiceServer val sourceLine = voiceServer.sourceLineManager .getLineByName("proximity") .orElseThrow { IllegalStateException("Proximity source line not found") } val source = sourceLine.createBroadcastSource(false) val player = voiceServer.playerManager .getPlayerByName("Apehum") .orElseThrow { IllegalStateException("Player not found") } val players: MutableSet = HashSet() players.add(player) source.players = players ``` ``` -------------------------------- ### Decrypt and Decode Audio Frame (Kotlin) Source: https://plasmovoice.com/docs/api/encoding-and-encryption.html This Kotlin code snippet shows how to decrypt an AES-encrypted frame and decode it using Opus to get a raw PCM audio frame. Close the AudioDecoder to prevent memory leaks. ```kotlin val voiceServer: PlasmoVoiceServer = /**/; val encryptedFrame: ByteArray = /**/; val decoder = voiceServer.createOpusDecoder(false) val encryption = voiceServer.defaultEncryption try { val decryptedFrame = encryption.decrypt(encryptedFrame) val audioFrame = decoder.decode(decryptedFrame) // ... } catch (e: CodecException) { throw RuntimeException("Failed to decrypt audio frame", e) } catch (e: EncryptionException) { throw RuntimeException("Failed to decode audio frame", e) } decoder.close() ``` -------------------------------- ### Initialize Proximity Server Activation Helper (Java) Source: https://plasmovoice.com/docs/api/activations.html Initializes the ProximityServerActivationHelper by providing the PlasmoVoiceServer instance, an activation, and a source line. Listeners are registered upon initialization and unregistered upon shutdown. ```java package com.plasmovoice.testaddon; import su.plo.voice.api.addon.AddonInitializer; import su.plo.voice.api.addon.InjectPlasmoVoice; import su.plo.voice.api.addon.annotation.Addon; import su.plo.voice.api.server.PlasmoVoiceServer; import su.plo.voice.api.server.audio.capture.ProximityServerActivationHelper; import su.plo.voice.api.server.audio.capture.ServerActivation; import su.plo.voice.api.server.audio.line.ServerSourceLine; @Addon( id = "pv-addon-test", name = "Test Addon", version = "1.0.0", authors = {"Plasmo"} ) public final class TestAddon implements AddonInitializer { @InjectPlasmoVoice private PlasmoVoiceServer voiceServer; private ProximityServerActivationHelper proximityHelper; @Override public void onAddonInitialize() { ServerActivation activation = /* register or get activation */; ServerSourceLine sourceLine = /* register or get source line */; this.proximityHelper = new ProximityServerActivationHelper(voiceServer, activation, sourceLine); proximityHelper.registerListeners(this); } @Override public void onAddonShutdown() { if (proximityHelper != null) { proximityHelper.unregisterListeners(this); } } } ``` -------------------------------- ### Create Static Source in Java Source: https://plasmovoice.com/docs/api/sources.html Attaches a static audio source to a specific world position. Ensure the source line and world are correctly initialized. ```java PlasmoVoiceServer voiceServer = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager() .getLineByName("proximity") .orElseThrow(() -> new IllegalStateException("Proximity source line not found")); McServerWorld world = voiceServer.getMinecraftServer() .getWorlds() .stream() .filter(w -> w.getName().equals("world")) .findAny() .orElseThrow(() -> new IllegalStateException("World not found")); ServerPos3d position = new ServerPos3d(world, 0.0D, 64.0D, 0.0D); ServerStaticSource source = sourceLine.createStaticSource(position, false); // You can also change the source position after it has been created: ServerPos3d newPosition = new ServerPos3d(world, 10.0D, 64.0D, 0.0D); source.setPosition(newPosition); ``` -------------------------------- ### Registering a Source Line Source: https://plasmovoice.com/docs/api/source-lines.html Use this to register a new source line with specified properties like name, icon, and priority. Ensure an addon is created beforehand. ```java PlasmoVoiceServer voiceServer = /* */; Object addon = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager().createBuilder( addon, "priority", // name "pv.activation.priority", // translation key "plasmovoice:textures/icons/speaker_priority.png", // icon resource location 10 // weight ).build(); ``` ```kotlin val voiceServer: PlasmoVoiceServer val addon: Any val sourceLine = voiceServer.sourceLineManager.createBuilder( addon, "priority", // name "pv.activation.priority", // translation key "plasmovoice:textures/icons/speaker_priority.png", // icon resource location 10 // weight ).build() ``` -------------------------------- ### Registering a Server Activation with InputStream Icon - Java Source: https://plasmovoice.com/docs/api/activations.html Register a server activation using an InputStream for the icon. This method requires handling potential IOExceptions during file access. ```java PlasmoVoiceServer voiceServer = /* */; Object addon = /* */; try (InputStream is = Files.newInputStream(new File("icon.png").toPath())) { ServerActivation activation = voiceServer.getActivationManager().createBuilder( addon, "priority", // name "pv.activation.priority", // translation key is, // icon "pv.activation.priority", // permission 10 // weight ).build(); } catch (IOException e) { e.printStackTrace(); } ``` -------------------------------- ### Initialize Minestom Voice Server (Java) Source: https://plasmovoice.com/docs/server/minestom Initialize the Minestom voice server on server startup and shut it down on server shutdown. A configuration directory will be created. ```java // ... on server startup File voiceConfigDirectory = new File("plasmovoice"); MinestomVoiceServer voiceServer = new MinestomVoiceServer(voiceConfigDirectory); voiceServer.onInitialize(); // ... on server shutdown voiceServer.onShutdown(); ``` -------------------------------- ### Player Activation Info Source: https://plasmovoice.com/docs/api/sources.html When handling activation and sending audio to a source, PlayerActivationInfo should be added to inform addons like pv-addon-replaymod about the destination source of the current activation. ```APIDOC ## Player Activation Info ### Description When handling activation and sending audio to a source, PlayerActivationInfo should be added to inform addons like pv-addon-replaymod about the destination source of the current activation. ### Java Example ```java ServerActivation activation = /* Initialize ServerActivation */; ServerProximitySource source = /* Initialize ServerProximitySource */; activation.onPlayerActivation((player, packet) -> { source.sendAudioFrame( packet.getData(), packet.getSequenceNumber(), packet.getDistance(), new PlayerActivationInfo(player, packet) ); return ServerActivation.Result.HANDLED; }); ``` ### Kotlin Example ```kotlin val activation: ServerActivation = /* Initialize ServerActivation */ val source: ServerProximitySource<*> = /* Initialize ServerProximitySource */ activation.onPlayerActivation { player, packet -> source.sendAudioFrame( packet.data, packet.sequenceNumber, packet.distance, PlayerActivationInfo(player, packet) ) ServerActivation.Result.HANDLED } ``` ``` -------------------------------- ### Create Static Source Source: https://plasmovoice.com/docs/api/sources.html Attaches a static audio source to a specific position in the world. ```APIDOC ## Create Static Source ### Description Creates a static audio source at a given world position. ### Method ```java ServerStaticSource source = sourceLine.createStaticSource(position, false); ``` ### Parameters - `position` (ServerPos3d) - The world position for the source. - `boolean` - Unknown purpose, likely related to initial configuration. ### Request Example (Java) ```java PlasmoVoiceServer voiceServer = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager().getLineByName("proximity").orElseThrow(() -> new IllegalStateException("Proximity source line not found")); McServerWorld world = voiceServer.getMinecraftServer().getWorlds().stream().filter(w -> w.getName().equals("world")).findAny().orElseThrow(() -> new IllegalStateException("World not found")); ServerPos3d position = new ServerPos3d(world, 0.0D, 64.0D, 0.0D); ServerStaticSource source = sourceLine.createStaticSource(position, false); ``` ### Request Example (Kotlin) ```kotlin val voiceServer: PlasmoVoiceServer val sourceLine = voiceServer.sourceLineManager.getLineByName("proximity").orElseThrow { IllegalStateException("Proximity source line not found") } val world = voiceServer.minecraftServer.worlds.find { it.name == "world" } ?: throw IllegalStateException("World not found") val position = ServerPos3d(world, 0.0, 64.0, 0.0) val source = sourceLine.createStaticSource(position, false) ``` ### Modifying Position ```java // You can also change the source position after it has been created: ServerPos3d newPosition = new ServerPos3d(world, 10.0D, 64.0D, 0.0D); source.setPosition(newPosition); ``` ```kotlin // You can also change the source position after it has been created: val newPosition = ServerPos3d(world, 10.0, 64.0, 0.0) source.position = newPosition ``` ``` ```APIDOC ## Create Entity Source ### Description Attaches an audio source to a specific entity. ### Method ```java ServerEntitySource source = sourceLine.createEntitySource(entity, false); ``` ### Parameters - `entity` (McServerEntity) - The entity to attach the source to. - `boolean` - Unknown purpose, likely related to initial configuration. ### Request Example (Java) ```java PlasmoVoiceServer voiceServer = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager().getLineByName("proximity").orElseThrow(() -> new IllegalStateException("Proximity source line not found")); Object platformEntity = /* platform-specific entity instance, e.g org.bukkit.entity.Entity */; McServerEntity entity = voiceServer.getMinecraftServer().getEntityByInstance(platformEntity); ServerEntitySource source = sourceLine.createEntitySource(entity, false); ``` ### Request Example (Kotlin) ```kotlin val voiceServer: PlasmoVoiceServer val sourceLine = voiceServer.sourceLineManager.getLineByName("proximity").orElseThrow { IllegalStateException("Proximity source line not found") } val platformEntity: Any = /* platform-specific entity instance, e.g org.bukkit.entity.Entity */ val entity = voiceServer.minecraftServer.getEntityByInstance(platformEntity) val source = sourceLine.createEntitySource(entity, false) ``` ``` ```APIDOC ## Create Player Source ### Description Attaches an audio source to a specific player, allowing audio to be played around them within a certain distance. ### Method ```java ServerPlayerSource source = sourceLine.createPlayerSource(voicePlayer, false); ``` ### Parameters - `voicePlayer` (VoiceServerPlayer) - The player to attach the source to. - `boolean` - Unknown purpose, likely related to initial configuration. ### Request Example (Java) ```java PlasmoVoiceServer voiceServer = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager().getLineByName("proximity").orElseThrow(() -> new IllegalStateException("Proximity source line not found")); VoiceServerPlayer voicePlayer = voiceServer.getPlayerManager().getPlayerByName("Apehum").orElseThrow(() -> new IllegalStateException("Player not found")); ServerPlayerSource source = sourceLine.createPlayerSource(voicePlayer, false); ``` ### Request Example (Kotlin) ```kotlin val voiceServer: PlasmoVoiceServer val sourceLine = voiceServer.sourceLineManager.getLineByName("proximity").orElseThrow { IllegalStateException("proximity source line not found") } val voicePlayer = voiceServer.playerManager.getPlayerByName("Apehum").orElseThrow { IllegalStateException("player not found") } val source = sourceLine.createPlayerSource(voicePlayer, false) ``` ``` ```APIDOC ## Remove Player Source Filter ### Description Removes a filter from a player source. By default, player sources have filters that prevent the attached player from hearing the audio. Removing the first filter overrides this behavior. ### Method ```java source.removeFilter(filter); ``` ### Parameters - `filter` (SourceFilter) - The filter to remove. Typically obtained via `source.getFilters().stream().findFirst()`. ### Request Example (Java) ```java ServerPlayerSource source = /* */; source.getFilters().stream().findFirst().ifPresent(source::removeFilter); ``` ### Request Example (Kotlin) ```kotlin val source: ServerPlayerSource = /* */ source.removeFilter(source.filters.first()) ``` ### Warning Removing all filters will disable vanish support for this source. ``` -------------------------------- ### Create Direct Source (Java) Source: https://plasmovoice.com/docs/api/sources.html Creates a direct audio source attached to a specific player. This source is only audible to the assigned player. Ensure the source line and player exist before creation. ```java PlasmoVoiceServer voiceServer = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager() .getLineByName("proximity") .orElseThrow(() -> new IllegalStateException("Proximity source line not found")); VoicePlayer voicePlayer = voiceServer.getPlayerManager() .getPlayerByName("Apehum") .orElseThrow(() -> new IllegalStateException("Player not found")); ServerDirectSource source = sourceLine.createDirectSource(voicePlayer, false); ``` -------------------------------- ### Create Direct Source (Kotlin) Source: https://plasmovoice.com/docs/api/sources.html Creates a direct audio source attached to a specific player. This source is only audible to the assigned player. Ensure the source line and player exist before creation. ```kotlin val voiceServer: PlasmoVoiceServer val sourceLine = voiceServer.sourceLineManager .getLineByName("proximity") .orElseThrow { IllegalStateException("Proximity source line not found") } val voicePlayer: VoicePlayer = voiceServer.playerManager .getPlayerByName("Apehum") .orElseThrow { IllegalStateException("Player not found") } val source = sourceLine.createDirectSource(voicePlayer, false) ``` -------------------------------- ### Create Entity Source in Java Source: https://plasmovoice.com/docs/api/sources.html Attaches an audio source to a specific entity in the game world. Requires a platform-specific entity instance. ```java PlasmoVoiceServer voiceServer = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager() .getLineByName("proximity") .orElseThrow(() -> new IllegalStateException("Proximity source line not found")); Object platformEntity = /* platform-specific entity instance, e.g org.bukkit.entity.Entity */; McServerEntity entity = voiceServer.getMinecraftServer().getEntityByInstance(platformEntity); ServerEntitySource source = sourceLine.createEntitySource(entity, false); ``` -------------------------------- ### Create Static Source in Kotlin Source: https://plasmovoice.com/docs/api/sources.html Attaches a static audio source to a specific world position. Ensure the source line and world are correctly initialized. ```kotlin val voiceServer: PlasmoVoiceServer val sourceLine = voiceServer.sourceLineManager .getLineByName("proximity") .orElseThrow { IllegalStateException("Proximity source line not found") } val world = voiceServer.minecraftServer.worlds .find { it.name == "world" } ?: throw IllegalStateException("World not found") val position = ServerPos3d(world, 0.0, 64.0, 0.0) val source = sourceLine.createStaticSource(position, false) // You can also change the source position after it has been created: val newPosition = ServerPos3d(world, 10.0, 64.0, 0.0) source.position = newPosition ``` -------------------------------- ### Handle Player Activation with Source Info (Kotlin) Source: https://plasmovoice.com/docs/api/sources.html Sends audio frames with PlayerActivationInfo to a source during player activation. This allows addons to identify the destination source for the current activation. ```kotlin val activation: ServerActivation = /* */ val source: ServerProximitySource<*> = /* */ activation.onPlayerActivation { player, packet -> source.sendAudioFrame( packet.data, packet.sequenceNumber, packet.distance, PlayerActivationInfo(player, packet) ) ServerActivation.Result.HANDLED } ``` -------------------------------- ### Registering a Server Activation - Java Source: https://plasmovoice.com/docs/api/activations.html Use this to register a new server activation with a specified name, translation key, icon resource, permission, and weight. Ensure you have a PlasmoVoiceServer instance and an addon object. ```java PlasmoVoiceServer voiceServer = /* */; Object addon = /* */; ServerActivation activation = voiceServer.getActivationManager().createBuilder( addon, "priority", // name "pv.activation.priority", // translation key "plasmovoice:textures/icons/microphone_priority.png", // icon resource location "pv.activation.priority", // permission 10 // weight ).build(); ``` -------------------------------- ### Registering a Server Activation - Kotlin Source: https://plasmovoice.com/docs/api/activations.html Use this to register a new server activation with a specified name, translation key, icon resource, permission, and weight. Ensure you have a PlasmoVoiceServer instance and an addon object. ```kotlin val voiceServer: PlasmoVoiceServer val addon: Any val activation = voiceServer.activationManager.createBuilder( addon, "priority", // name "pv.activation.priority", // translation key "plasmovoice:textures/icons/microphone_priority.png", // icon resource location "pv.activation.priority", // permission 10 // weight ).build() ``` -------------------------------- ### Create Player Source in Java Source: https://plasmovoice.com/docs/api/sources.html Attaches an audio source to a specific player, making audio audible around them. Requires a valid VoiceServerPlayer instance. ```java PlasmoVoiceServer voiceServer = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager() .getLineByName("proximity") .orElseThrow(() -> new IllegalStateException("Proximity source line not found")); VoiceServerPlayer voicePlayer = voiceServer.getPlayerManager() .getPlayerByName("Apehum") .orElseThrow(() -> new IllegalStateException("Player not found")); ServerPlayerSource source = sourceLine.createPlayerSource(voicePlayer, false); ``` -------------------------------- ### Create Broadcast Source (Java) Source: https://plasmovoice.com/docs/api/sources.html Creates a broadcast audio source audible to a group of players. By default, it's audible to all players. Use setPlayers to specify a subset. Ensure the source line exists. ```java PlasmoVoiceServer voiceServer = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager() .getLineByName("proximity") .orElseThrow(() -> new IllegalStateException("Proximity source line not found")); ServerBroadcastSource source = sourceLine.createBroadcastSource(false); // Set "listeners" for broadcast source VoicePlayer player = voiceServer.getPlayerManager() .getPlayerByName("Apehum") .orElseThrow(() -> new IllegalStateException("Player not found")); Set players = new HashSet<>(); players.add(player); source.setPlayers(players); ``` -------------------------------- ### Create Entity Source in Kotlin Source: https://plasmovoice.com/docs/api/sources.html Attaches an audio source to a specific entity in the game world. Requires a platform-specific entity instance. ```kotlin val voiceServer: PlasmoVoiceServer val sourceLine = voiceServer.sourceLineManager .getLineByName("proximity") .orElseThrow { IllegalStateException("Proximity source line not found") } val platformEntity: Any = /* platform-specific entity instance, e.g org.bukkit.entity.Entity */ val entity = voiceServer.minecraftServer.getEntityByInstance(platformEntity) val source = sourceLine.createEntitySource(entity, false) ``` -------------------------------- ### Create Player Source in Kotlin Source: https://plasmovoice.com/docs/api/sources.html Attaches an audio source to a specific player, making audio audible around them. Requires a valid VoiceServerPlayer instance. ```kotlin val voiceServer: PlasmoVoiceServer val sourceLine = voiceServer.sourceLineManager .getLineByName("proximity") .orElseThrow { IllegalStateException("proximity source line not found") } val voicePlayer = voiceServer.playerManager .getPlayerByName("Apehum") .orElseThrow { IllegalStateException("player not found") } val source = sourceLine.createPlayerSource(voicePlayer, false) ``` -------------------------------- ### Import Custom Javascript Placeholder Source: https://plasmovoice.com/docs/server/papi Configures PlaceholderAPI to recognize the custom JavaScript script by adding an entry to `javascript_placeholders.yml`. ```yaml check_voice: file: check_voice.js ``` -------------------------------- ### Registering a Server Activation with InputStream Icon - Kotlin Source: https://plasmovoice.com/docs/api/activations.html Register a server activation using an InputStream for the icon. This method utilizes Kotlin's use function for automatic stream closing. ```kotlin val voiceServer: PlasmoVoiceServer val addon: Any val activation = Files.newInputStream(File("icon.png").toPath()).use { icon -> voiceServer.activationManager.createBuilder( this, "priority", // name "pv.activation.priority", // translation key icon, // icon "pv.activation.priority", // permission 10 // weight ).build() } ``` -------------------------------- ### Obtaining an Existing Activation by Name - Java Source: https://plasmovoice.com/docs/api/activations.html Retrieve an existing server activation by its name. Throws an IllegalStateException if the activation is not found. ```java PlasmoVoiceServer voiceServer = /* */; ServerActivation activation = voiceServer.getActivationManager() .getActivationByName("proximity") .orElseThrow(() -> new IllegalStateException("Proximity activation not found")); ``` -------------------------------- ### Create Broadcast Source (Kotlin) Source: https://plasmovoice.com/docs/api/sources.html Creates a broadcast audio source audible to a group of players. By default, it's audible to all players. Use players property to specify a subset. Ensure the source line exists. ```kotlin val voiceServer: PlasmoVoiceServer val sourceLine = voiceServer.sourceLineManager .getLineByName("proximity") .orElseThrow { IllegalStateException("Proximity source line not found") } val source = sourceLine.createBroadcastSource(false) val player = voiceServer.playerManager .getPlayerByName("Apehum") .orElseThrow { IllegalStateException("Player not found") } val players: MutableSet = HashSet() players.add(player) source.players = players ``` -------------------------------- ### Send Audio Samples with ArrayAudioFrameProvider (Kotlin) Source: https://plasmovoice.com/docs/api/sources.html Use ArrayAudioFrameProvider to send a fixed set of audio samples to a specific distance. Ensure to close the provider and remove the source when done to prevent memory leaks. ```kotlin package com.plasmovoice.testaddon import su.plo.voice.api.server.PlasmoVoiceServer import su.plo.voice.api.server.audio.provider.ArrayAudioFrameProvider import su.plo.voice.api.server.audio.source.ServerPlayerSource class TestAudioSender { /** * Sends the audio samples to an audio source in specified distance. * * @param voiceServer Plasmo Voice Server API. * @param source The audio source to send audio. * @param samples 48kHz 16-bit mono audio samples. * @param distance The distance to send audio. */ private fun sendPacketsToSource( voiceServer: PlasmoVoiceServer, source: ServerPlayerSource, samples: ShortArray, distance: Short ) { val frameProvider = ArrayAudioFrameProvider(voiceServer, false) val audioSender = source.createAudioSender(frameProvider, distance) // or // AudioSender audioSender = source.createAudioSender(frameProvider); // if you are using direct or broadcast source frameProvider.addSamples(samples) audioSender.start() audioSender.onStop { // you need to close the ArrayAudioFrameProvider, // because it contains the encoder which should be closed after being used, // otherwise it will cause a memory leak frameProvider.close() // you also need to remove the source from the source line // if you don't need it after the audio was sent source.remove() } // you can also manually stop the AudioSender: // audioSender.stop() // or pause/resume: // audioSender.pause() // audioSender.resume() } } ``` -------------------------------- ### Adding players to the overlay Source: https://plasmovoice.com/docs/api/source-lines.html Configures a source line to manage player synchronization and demonstrates how to add players to a broadcast set. ```APIDOC ## Players in overlay ### Description Enables and utilizes player synchronization features within a source line. This allows for managing which players are associated with a specific audio source, often for features like proximity chat or broadcasts. ### Method `ServerSourceLine.Builder#withPlayers(true)` followed by `sourceLine.getPlayerSetManager()` ### Parameters #### `withPlayers` Parameter - **enable** (Boolean) - Set to `true` to enable player synchronization for the source line. ### Request Example (Java) ```java PlasmoVoiceServer voiceServer = /* */; ServerSourceLine sourceLine = voiceServer.getSourceLineManager() .createBuilder(/*...*/) .withPlayers(true) .build(); VoicePlayer player = /* voice player */; ServerPlayerSetManager playerSetManager = sourceLine.getPlayerSetManager(); ServerPlayerSet broadcastSet = playerSetManager.createBroadcastSet(); broadcastSet.addPlayer(player); // sets the broadcast player set to the player playerSetManager.setPlayerSet(player, broadcastSet); ``` ### Request Example (Kotlin) ```kotlin val voiceServer: PlasmoVoiceServer val sourceLine: ServerSourceLine = voiceServer.sourceLineManager .createBuilder(/* */) .withPlayers(true) .build() val player: VoicePlayer /* voice player */ val playerSetManager = sourceLine.playerSetManager val broadcastSet = playerSetManager.createBroadcastSet() broadcastSet.addPlayer(player) // sets the broadcast player set to the player playerSetManager.setPlayerSet(player, broadcastSet) ``` ``` -------------------------------- ### Implement Custom Audio Frame Provider in Java Source: https://plasmovoice.com/docs/api/sources.html Implement the AudioFrameProvider interface to provide custom audio frames. Ensure frames are encoded and encrypted before returning. Use AudioFrameResult.EndOfStream or AudioFrameResult.Finished to manage stream state. ```java package com.plasmovoice.testaddon; import su.plo.voice.api.server.PlasmoVoiceServer; import su.plo.voice.api.server.audio.provider.AudioFrameProvider; import su.plo.voice.api.server.audio.provider.AudioFrameResult; import su.plo.voice.api.server.audio.source.AudioSender; import su.plo.voice.api.server.audio.source.ServerPlayerSource; public final class CustomFrameProvider { /** * Sends the audio samples to an audio source in specified distance. * * @param source The audio source to send audio. * @param distance The distance to send audio. */ private void createCustomFrameProvider( ServerPlayerSource source, short distance ) { TestAudioFrameProvider frameProvider = new TestAudioFrameProvider(); AudioSender audioSender = source.createAudioSender(frameProvider, distance); // or // AudioSender audioSender = source.createAudioSender(frameProvider); // if you are using direct or broadcast source audioSender.start(); audioSender.onStop(() -> { // you also need to remove the source from the source line // if you don't need it after the audio was sent source.remove(); }); } public class TestAudioFrameProvider implements AudioFrameProvider { @Override public AudioFrameResult provide20ms() { if (/* end of the audio stream*/ ) { // [EndOfStream] means that [AudioFrameProvider] reaches the end of the current stream, // but not completely closed and frames could be sent later return AudioFrameResult.EndOfStream.INSTANCE; } else if (/* audio stream finished */) { // [Finished] means that [AudioFrameProvider] is completely closed and will not return any frames return AudioFrameResult.Finished.INSTANCE; } // null frame means that frame is not ready yet // return new AudioFrameResult.Provided(null); byte[] encryptedFrame = new byte[100]; return new AudioFrameResult.Provided(encryptedFrame); } } } ```