Skip to content
marchell edited this page Aug 2, 2025 · 4 revisions

Audio

Playing audio clips is simple. All you need is your own AudioHandler instance.

Creating audio handlers

using LabExtended.API.Audio;

public void CreateHandler()
{
    // Retrieves our AudioHandler (if we've already created one) 
    // OR creates a new one (in which case the delegate in the second parameter is called)
    AudioHandler handler = AudioManager.GetOrAddHandler("ExampleHandler", handler =>
    {
        // Adds an AudioPlayer instance to your AudioHandler instance.
        handler.AddPlayer();

        // Adds a SpeakerToy instance to your AudioHandler instance.
        // The first parameter is the name of the SpeakerToy, used only in the AudioHandler.
        // The second parameter is the controller ID of the SpeakerToy, which is used when sending audio to players.
        // - Assigning the same ID to multiple speakers results in the same audio playing from all speakers with this ID.
        handler.EnsureSpeaker("ExampleSpeaker", 1);
    });

    // Lets also load the clip that we want to play.
    // It's important to use the same name as the file in the method's first argument (this includes any extensions).
    KeyValuePair<string, byte[]> clip = AudioManager.GetClip("ExampleClip");
}

Using audio handlers

Forwarding voice chat packets

// This is called every time a player sends a voice chat packet to the server.
public void OnSpeaking(PlayerSendingVoiceChatMessageEventArgs args)
{
    // You should save your AudioHandler instance as a field or a property, this event is called A LOT
    if (!AudioManager.TryGetHandler("ExampleHandler", out var handler))
        return;

    // Since this is a LabAPI events and not a custom LabExtended event, we need to cast the LabAPI player instance
    // into our custom subtype ExPlayer instance.
    if (args.Speaker is not ExPlayer speaker)
        return;

    // This will send the voice chat message through each speaker you defined in your AudioHandler.
    handler.Transmit(args.Messsage.Data, args.Message.DataLength);

    // This will send the voice chat message through each speaker to players that are within 20 meters of the speaking player.
    handler.Transmit(args.Messsage.Data, args.Message.DataLength, player => player.Position.DistanceTo(speaker) <= 20f);
}

Playing audio clips

public void PlayClip()
{
    // Will return false if no AudioHandler with name "ExampleHandler" is found.
    if (!AudioManager.TryGetHandler("ExampleHandler", out var handler))
        return;

    // Will return false if no audio clip file with name "ExampleClip" is found.
    if (!AudioManager.TryGetClip("ExampleClip", out var clip))
        return;

    // Will start playing the audio clip (or put it into the queue if something is playing)
    // Audio of the clip will be sent through each speaker ID defined in your AudioHandler.
    handler.Player.Play(clip, false);

    // You can also start playback of an audio clip at a specific location.
    handler.PlayAt(clip, Vector3.zero);

    // OR you can start playback of an audio clip with a specific Transform!
    handler.PlayOn(clip, ExPlayer.Players.RandomItem().CameraTransform);
}

Collections

LabExtended adds some custom collections, mostly for the purpose of optimization and convenience.

UnsafeList

This is just a normal List<T> from System.Collections.Generic, the only difference is that ALL "safety" mechanisms have been removed, which means

  • If you remove an item while enumerating the list, no exceptions will be thrown BUT you may skip some items.

UpdateableList

This collection (a subtype of UnsafeList<T>) calls an update method on each element every frame with a configurable delay. Each element must be a subtype of the IUpdateableElement interface.

Custom Toggles

The SwitchContainer class is used to configure custom player functions, which range from changing Remote Admin visibility to to the ability to configure position synchronization.

  • Commands can be used to change values of these switches.

Custom Ammo

  • Custom Ammo is stored in a very simple way, as a developer you only need to respect ammo IDs of other plugins (ammo ID is stored as a 16-bit unsigned integer!)
  • Commands can also be used to list and manage custom ammo.
public void AddAmmo(ExPlayer player)
{
    // This will add 500 bullets of ammo with the ID 10
    player.Inventory.CustomAmmo.Add(10, 500);
}

Custom Effects

LabExtended provides a very basic API for Custom Effects.

  • Custom Effects do not have a name nor an ID
    • Instead, they are listed by their type name.
  • Custom Effects are stored in the EffectContainer class.
  • Custom Effects are automatically registered by the loader (unless you specify a LoaderIgnore attribute).
    • To manually register a Custom Effect, just add the effect's type to the CustomPlayerEffect.Effects list.
  • Custom Effects have a separate instance for every player, hence why registration uses only the type.
  • Custom Effects can also be managed by commands.

Definition of a Custom Effect

The structure of a Custom Effect class shoud look like this:

public class BoostEffect : CustomPlayerEffect
{
    // You can also override methods like Start() to initialize your Custom Effect, but for the sake of simplicity we'll just apply it.
    // This method is called once something calls the effect's Enable() method.
    public override void ApplyEffects()
    {
        // This will set the intensity of the MovementBoost effect to 255.
        Player.Effects.MovementBoost.Intensity = 255;
    }

    // This method is called once something calls the effect's Disable() method.
    public override void RemoveEffects()
    {
        // This will set the intensity of the MovementBoost effect to 0.
        Player.Effects.MovementBoost.Intensity = 0;
    }
}

Activating / Deactivating a Custom Effect

public void ActivateBoost(ExPlayer player)
{
    // Attempts to find a previously registered Custom Effect by it's type, in this case BoostEffect.
    if (player.Effects.TryGetCustomEffect<BoostEffect>(out var boostEffect))
    {
       // Enables our custom effect by calling it's ApplyEffects() method.
       boostEffect.Enable();
    }
}

// You can disable the effect in the same way.
public void DisableBoost(ExPlayer player)
{
    // Attempts to find a previously registered Custom Effect by it's type, in this case BoostEffect.
    if (player.Effects.TryGetCustomEffect<BoostEffect>(out var boostEffect))
    {
       // Disables our custom effect by calling it's RemoveEffects() method.
       boostEffect.Disable();
    }
}

Updating a Custom Effect

If you need your Custom Effect to update every frame, you can use the CustomTickingEffect subtype.

  • This class provides an overridable Tick() method
  • You can also set a custom delay (in seconds) between each Tick() method call by overriding the Delay property.

Duration of a Custom Effect

If you want a duration for your Custom Effect, you can use the CustomDurationEffect subtype.

  • This class provides an overridable GetDuration() method, in which you should calculate the effect's duration in seconds.
  • CheckDuration() method is called every time before the effect ends in case you need to add duration.
  • RemainingDuration can be used to check the effect's remaining duration (in seconds).