Client audio: Difference between revisions

From Wiki G1R-MP G1 Remake Multiplayer
Jump to navigation Jump to search
Add client audio guide for update 0.1.2
 
Document expanded scripting capacities and complete audited limits for 0.1.3 BUILD81
 
Line 111: Line 111:
== Ownership and limits ==
== Ownership and limits ==


* A resource can own at most 32 active sounds; the client runtime can own at most 128 in total.
* From update 0.1.3 BUILD81, a resource can own at most 128 active sounds; the client runtime can own at most 512 in total (previously 32/128). See [[Scripting limits]].
* A handle can be controlled only by the resource that created it.
* A handle can be controlled only by the resource that created it.
* Stopping a resource releases all sounds that it still owns.
* Stopping a resource releases all sounds that it still owns.

Latest revision as of 11:51, 12 September 2026


AVAILABLE FROM UPDATE 0.1.2
Resource-owned OGG Vorbis playback, including positional 3D sound, is available in G1R:MP 0.1.2 and later.

Client audio lets a client-side Lua resource play non-spatial interface sounds and positional world sounds. Audio handles belong to the resource that created them and cannot be controlled by another resource.

Supported files

The first release accepts Ogg containers containing Vorbis audio. Ogg Opus and other codecs are not accepted. Every sound must be declared as a join-time file in the same resource:

<?xml version="1.0" encoding="utf-8"?>
<scripts>
    <script src="client.lua" type="client" cache="true" />
    <file src="sounds/notification.ogg" download="join" />
    <file src="sounds/forge.ogg" download="join" />
</scripts>

The runtime accepts only the exact relative path declared by the current resource. URLs, absolute paths, traversal with .., reparse-point escapes, undeclared files, and files owned by another resource are rejected. File size and SHA-256 are checked again against the signed resource catalog before decoding.

Audio functions

Playback quality and output device

2D playback preserves the source channel layout, so stereo material remains stereo. A positional source is downmixed to mono because it represents one point in the world. When source and output sample rates differ, the runtime converts once with the bundled libsamplerate 0.2.2 sinc converter using SRC_SINC_MEDIUM_QUALITY and bypasses miniaudio's pitch-oriented linear converter. Matching sample rates are not resampled.

Resource audio and voice playback use the normal Windows default playback device. The Windows communications-device selection is used only for voice microphone capture; it does not redirect or reduce the quality of 2D or 3D playback.

For 3D sound, volume remains full through minDistance, then fades linearly and is silent at and beyond maxDistance. Coordinates and distances use metres. The listener follows the local native player while at least one positional sound exists.

Local playback

local notification = playSound("sounds/notification.ogg", false, 0.65)

local forge = playSound3D(
    "sounds/forge.ogg",
    125.5, -48.0, 3.25,
    true, 0.8,
    2.0, 45.0
)

if forge then
    setSound3DPosition(forge, 126.0, -48.0, 3.25)
    setSound3DMinMaxDistance(forge, 2.0, 60.0)
end

Playing the same 3D sound for every player

Audio functions are client-side. To initiate a world sound authoritatively, the server sends a validated event to the client scripts. Using root as the target broadcasts the same reliable ordered event to every connected player.

Calculate the source position once on the server and send identical world coordinates to all clients. Do not recalculate the source from each receiving player's position.

server.lua

addCommandHandler("forgeaudio", function(playerId)
    local position = getPlayerPosition(playerId)
    if not position then
        return
    end

    triggerClientEvent(
        root,
        "forgeAudio:play",
        resourceRoot,
        position.x + 4.0,
        position.y,
        position.z
    )
end)

client.lua

local forgeSound = false

addEvent("forgeAudio:play", true)
addEventHandler("forgeAudio:play", resourceRoot, function(x, y, z)
    if forgeSound then
        stopSound(forgeSound)
    end

    forgeSound = playSound3D(
        "sounds/forge.ogg",
        x, y, z,
        true, 0.8,
        1.0, 30.0
    )
end)

Each client creates its own local handle. Stopping, moving, pausing, or changing a shared sound therefore requires another server event broadcast to the same clients. Delivery is reliable and ordered, but playback start is not sample-accurate and can differ slightly due to network jitter.

Ownership and limits

  • From update 0.1.3 BUILD81, a resource can own at most 128 active sounds; the client runtime can own at most 512 in total (previously 32/128). See Scripting limits.
  • A handle can be controlled only by the resource that created it.
  • Stopping a resource releases all sounds that it still owns.
  • A stopped or finished handle must not be reused.
  • Validation and playback failures are recorded in the client runtime log.