Can't Get Music to be Client Side Only

(Posting on behalf of my friend and scripter I will relay information for them!)

Note: I’m experienced in programming but not Roblox’s platform. I’m trying to edit a game engine someone else wrote.

The code has one looping song for when in the avatar editing menu and a second song for when the player spawns in. Both songs are only playing on the server side of the game and not the client side. This means that whenever a new player joins the game, the menu music overlaps with the main music for a few seconds, and whenever a player spawns in, the music stops for a few seconds and restarts (again, for everyone in the server).

There’s three scripts that control the music. One to control the menu music when the player joins and stops it when the player spawns in. One starts the main music when the player spawns in. The third is tied to the GUI and turns off the music (this one does only work on the client-side). The scripts are organized like this because they do other things with initialization and spawning in the player. All three are local scripts. The first two (initialization and spawning in) are located under StarterPlayerScripts. The last one is in StarterGui.

Things I’ve tried:
-Moving the music files to SoundService, StarterPlayer, or ReplicatedStorage
-Taking the audio out of the SoundGroup they’re in

Code in the initialization script:

local MenuMusic = game.ReplicatedStorage.Music:WaitForChild("MusicGroup"):WaitForChild("MenuMusic")

player.CharacterAdded:Connect(function(char)
    MenuMusic.Volume = 0.5
    if not player.PlayerGui:WaitForChild("LoadingScreen", 1) and not MenuMusic.IsPlaying then
        player.MenuMusic:Play()
    end
end)

Code in the spawning script:

local MenuMusic = game.ReplicatedStorage.Music:WaitForChild("MusicGroup"):WaitForChild("MenuMusic")
local GameMusic = game.ReplicatedStorage.Music:WaitForChild("MusicGroup"):WaitForChild("GameMusic")

player.CharacterAdded:Connect(function(char)
    if IsSpawningValue and IsSpawningValue.Value == true then
        IsSpawningValue.Value = false
        --stop music, keep at the end
        local T = tick()
        local t = 0
        while t < 2 do
            MenuMusic.Volume = (1 - t/2)*0.3
            t = tick() - T
            wait()
        end
        MenuMusic:Stop()
        GameMusic.Volume = 0.3
        GameMusic:Play()
    else
        if GameMusic.IsPlaying then
            local T = tick()
            local t = 0
            while t < 2 do
                GameMusic.Volume = (1 - t/2)*0.3
                t = tick() - T
                wait()
            end
            GameMusic:Stop()
        end
    end
end)

Code in the GUI script:

local MusicGroup = game.ReplicatedStorage.Music:WaitForChild("MusicGroup")

script.Parent.Activated:Connect(function()
    if script.Parent.BackgroundColor3 == Color3.fromRGB(170, 0, 0) then
        MusicGroup.Volume = 1
        script.Parent.BackgroundColor3 = Color3.fromRGB(0, 170, 0)
    else
        MusicGroup.Volume = 0
        script.Parent.BackgroundColor3 = Color3.fromRGB(170, 0, 0)
    end
end)

Any help is appreciated!

this was a tricky one, but i assume you’re doing

game.Players.CharacterAdded

which fires everytime someone new joins, even on the client

so that explains why you’re having this issue, despite your scripts are LocalScripts

you should instead use
game.Players.LocalPlayer
because it references the player belonging to said client

(Relaying information for the scripter) It is already set to LocalPlayer on the relevant scripts, specifically:

local player = game.Players.LocalPlayer

and

local player = game:GetService("Players").LocalPlayer

in that case i think there might be interfering logic with the CharacterAdded events

it’s genuinely complicated for something that should be simple, i had a hard time figuring out what exactly is going on

instead, reference the menu & main music and play the menu music when the player joins, there is no need for CharacterAdded

then when the player spawns, pause the menu music and play the main music

you may keep the gui script

your code should look like this

local Players = game:GetService("Players")
local MusicGroup = game.ReplicatedStorage.Music:WaitForChild("MusicGroup")
local MenuMusic = MusicGroup.MenuMusic
local GameMusic = MusicGroup.GameMusic
MenuMusic.Volume = 0.5
MenuMusic:Play()

Players.LocalPlayer.CharacterAdded:Connect(function()
    MenuMusic:Stop()
    GameMusic:Play()
end)

I have not read any of this but the title , but 1 make sure the sound is a child of a part / mesh etc, and then that your script are local when it is told to play

Alright so an update:

They implemented the suggestion, working around the other nonsense that’s in the scripts, and now the menu music doesn’t switch over to the game music.

They put this bit at the top of the initialization script:

MenuMusic.Volume = 0.5
MenuMusic:Play()

And for this bit they added:

.LocalPlayer

Everywhere Players was used

Players.LocalPlayer.CharacterAdded:Connect(function()
    MenuMusic:Stop()
    GameMusic:Play()
end)

Excuse both of us if this is a little incoherent they have been trying to sort the previous coders mess all day (for some reason trying to fix just the music broke what should have been unrelated parts of the game) and it is very late for both of us now lol

Hey just for the record, sounds directly under the player replicate to everyone. To fix this, you’d wanna place the sound somewhere like in the PlayerGui.

1 Like

now the menu music doesn’t switch over to the game music.

can you tell me what exactly do you have now

this is not a problem if you’re playing the sounds via a LocalScript

Since it’s like really late where we are, we’ll have to send an update on “the disaster that is both scripts” tomorrow. Thank you so much for helping us get this far on fixing it we both appreciate it a lot!

1 Like

i wouldn’t be surpised if the previous scripter set the volume of GameMusic to 0 via the properties

They didn’t use what you said at all, where do you see that in their code?

I think the issue is that this isn’t the case…

They’ve just said they’re using Localscripts

I made an assumption which was proven false already

To be clear, the one and only problem is that the menu music plays for all players when a new player joins, when it’s only supposed to play for the player that just joined? Also then meaning the game sound works just fine^

It looks like this problem comes from the fact the Sound instance has Playing = true and gets replicated to all players. @thom463s did mention a fix about it replicating to all players instead of just the new player which I think could fix it. Probably easier and better would be to turn Playing = false and then also make the song play in the local script right away (not inside character added unless you want to wait for the character before playing the sound).

You explain that the initialization script is the one playing the sound. However, since there is no print in there to verify that it actually does just that, and the fact it checks that the sound isn’t already playing, then personally I’d have to guess this is a backup that makes sure the sound is playing, and if you make it the only thing making the sound play (like setting playing to false), then this should fix the problem since everything would then be handled client sided.
Since you don’t show full scripts this is my best bet on where that issue comes from

We checked the volume on the properties just in case and they are set to 0.3 or 0.5:

Playing = false is infact on both sounds

is there any server script interfering with the sounds?

(Relaying for the scripter again)

Alright so It’s probably a good idea to provide the full spaghetti code. At this point I’m thinking it may be easier to separate out the music into its own script but I’d have to figure out how to link it up with the player spawning in.
We paid a competent programmer a lot of Robux to clean up this code for another game, so if it would be helpful to see the un-spaghettified but still broken version that I haven’t messed with then we can send that also.

MiscScript:

local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local MenuMusic = game.ReplicatedStorage.Music:WaitForChild("MusicGroup"):WaitForChild("MenuMusic")
local debounce = false
local SaveModelEvent = game:GetService("ReplicatedStorage").Events:WaitForChild("SaveModelEvent")
local ChangeStateEvent = game.ReplicatedStorage.Events:WaitForChild("ChangeStateEvent")
local KillPlayerEvent = game.ReplicatedStorage.Events.KillPlayerEvent
local ToEditorEvent = player:WaitForChild("ToEditorEvent", 30)
local KillEvent = player:WaitForChild("KillEvent", 30)
MenuMusic.Volume = 0.5
MenuMusic:Play()
player.CharacterAdded:Connect(function(char)

    char:WaitForChild("Humanoid").Died:Connect(function()
        game.ReplicatedStorage.Events.KillPlayerEvent:FireServer()
    end)
    
    --death by void
    char.ChildRemoved:Connect(function()
        if not char:FindFirstChild("HumanoidRootPart") and not debounce then
            debounce = true
            char.Humanoid.Health = 0
            wait(1)
            debounce = false
        end
    end)
end)

game.Players.PlayerAdded:Connect(function(plr)
    plr.CharacterAdded:Connect(function(char)
        if char:WaitForChild("Head"):WaitForChild("OverheadDisplay", 20) then
            if player:FindFirstChild("HideBiosValue") and player.HideBiosValue.Value == true then
                char.Head.OverheadDisplay.Content.DescLabel.Visible = false
                char.Head.OverheadDisplay.Content.Position = UDim2.new(0, 0, 0.4, 0)
            end
            if player:FindFirstChild("HidePanelsValue") and player.HidePanelsValue.Value == true then
                char.Head.OverheadDisplay.Enabled = false
            end
        end
    end)
end)
--Add and prepare ClickDetectors when getting in the editor + various stuff
ToEditorEvent.Event:Connect(function()
    local playerModel = workspace.Util.PlayerModels:WaitForChild(player.Name.."'s model")
    local SelectedPart = player.PlayerGui:WaitForChild("ScreenGui"):WaitForChild("CustomizationFrame"):WaitForChild("LeftPanel"):WaitForChild("SelectedPart")
    local ColoringMode = player.PlayerGui.ScreenGui.CustomizationFrame.LeftPanel:WaitForChild("ColoringMode")
    
    workspace.CurrentCamera.CFrame = workspace.Util.EditorCameraPart.CFrame
    workspace.CurrentCamera.CameraType = Enum.CameraType.Follow
    workspace.CurrentCamera.CameraSubject = playerModel:WaitForChild("HumanoidRootPart")
    game:GetService("Lighting").Blur.Size = 0
    player.CameraMaxZoomDistance = 35
    player.CameraMinZoomDistance = 20
    player.CameraMinZoomDistance = 5
    
    ChangeStateEvent:FireServer("Editing")
    
    mouse.TargetFilter = playerModel.HumanoidRootPart
    mouse.Button1Down:Connect(function()
        if ColoringMode.Value == "Advanced" and mouse.Target.Parent == playerModel then
            SelectedPart.Value = mouse.Target
        end
    end)
end)

KillEvent.Event:Connect(function()
    KillPlayerEvent:FireServer()
end)

CharacterAddedScript:

local player = game:GetService("Players").LocalPlayer
local playerGUI = player.LocalPlayer:WaitForChild("PlayerGui")
local camera = workspace.CurrentCamera
local Blur = game:GetService("Lighting").Blur
Blur.Size = 16
local TopBarBGF = game:GetService("ReplicatedFirst"):WaitForChild("TopBarBackgroundFrame")
local MenuMusic = game.ReplicatedStorage.Music:WaitForChild("MusicGroup"):WaitForChild("MenuMusic")
local GameMusic = game.ReplicatedStorage.Music:WaitForChild("MusicGroup"):WaitForChild("GameMusic")
local dens = workspace.Dens:GetChildren()
local ChangeStateEvent = game.ReplicatedStorage.Events:WaitForChild("ChangeStateEvent")
local KillEvent = player.LocalPlayer:WaitForChild("KillEvent", 30)
local CurrentState = player.LocalPlayer:WaitForChild("CurrentStateValue", 30)

local function ToggleCore(CoreElement, state)
    local success = false
    while not success do
        success = pcall(function()
            game:GetService("StarterGui"):SetCore(CoreElement, state)
        end)
        if success then
            break
        else
            print("CoreGUI being slow for ", CoreElement)
            wait(.1)
        end
    end
end
player.LocalPlayer.CharacterAdded:Connect(function(char)
    TopBarBGF:Clone().Parent = playerGUI:WaitForChild("ScreenGui") --a frame to make the top bar with the menus green
    local IsSpawningValue = player.LocalPlayer:FindFirstChild("IsSpawningValue") --a boolean saying if the player is going to spawn as the model (true) or goint to the main menu (false)
    char:WaitForChild("Humanoid"):SetStateEnabled(Enum.HumanoidStateType.FallingDown, false)
    char.Humanoid:SetStateEnabled(Enum.HumanoidStateType.Ragdoll, false) --2 lines to prevent tripping and falling
    if IsSpawningValue and IsSpawningValue.Value == true then
        IsSpawningValue.Value = false
        playerGUI.ScreenGui.GameInterfaceFrame.Visible = true
        Blur.Size = 0
        
        ChangeStateEvent:FireServer("Playing")
        
        repeat wait()
            camera.CameraType = Enum.CameraType.Custom
        until camera.CameraType == Enum.CameraType.Custom
        
        for i = 1, #dens do
            if dens[i].OwnerName.Value == player.LocalPlayer.Name then
                playerGUI.ScreenGui.GameInterfaceFrame.DenCommandFrame.DoorButton.Active = true
                playerGUI.ScreenGui.GameInterfaceFrame.DenCommandFrame.LeaveButton.Active = true
                playerGUI.ScreenGui.GameInterfaceFrame.DenCommandFrame:TweenPosition(UDim2.new(1, -203, 1, -103), "Out", "Quad", 1, true)
                break
            end
        end
        
        --stop music, keep at the end
        local T = tick()
        local t = 0
        while t < 2 do
            MenuMusic.Volume = (1 - t/2)*0.3
            t = tick() - T
            wait()
        end
        MenuMusic:Stop()
        GameMusic.Volume = 0.3
        GameMusic:Play()
    else
        Blur.Size = 16
        repeat wait()
            camera.CameraType = Enum.CameraType.Scriptable
        until camera.CameraType == Enum.CameraType.Scriptable
        camera.CFrame = workspace.Util:WaitForChild("MenuCameraPart").CFrame
        
        ChangeStateEvent:FireServer("Main menu")
        
        playerGUI.ScreenGui.CustomizationFrame.Visible = false
        playerGUI.ScreenGui.GameInterfaceFrame.Visible = false
        playerGUI.ScreenGui.ModelSelectFrame.AgeSelectFrame.AgeSelectScript.Disabled = false
        playerGUI.ScreenGui.ModelSelectFrame.AgeSelectFrame.BackButton.BackScript.Disabled = false
        playerGUI.ScreenGui.ModelSelectFrame.SpeciesSelectFrame.SpeciesSelectScript.Disabled = false
        playerGUI.ScreenGui.ModelSelectFrame.SpeciesSelectFrame.Position = UDim2.new(0, 0, 0, 0)
        playerGUI.ScreenGui.ModelSelectFrame.SpeciesSelectFrame.Visible = true
        playerGUI.ScreenGui.ModelSelectFrame.LoadFrame.Visible = false
        playerGUI.ScreenGui.ModelSelectFrame.AgeSelectFrame.Position = UDim2.new(0, 0, 1, 0)
        playerGUI.ScreenGui.ModelSelectFrame.Visible = true
        
        ToggleCore("ResetButtonCallback", false)
        ToggleCore("TopbarEnabled", false)
        
        if GameMusic.IsPlaying then
            local T = tick()
            local t = 0
            while t < 2 do
                GameMusic.Volume = (1 - t/2)*0.3
                t = tick() - T
                wait()
            end
            GameMusic:Stop()
        end
    end
end)

--support for player deep freeze
local IsFrozen = player.LocalPlayer:WaitForChild("IsFrozenValue", 30)
IsFrozen.Changed:Connect(function(newVal)
    if newVal == true then
        ToggleCore("ResetButtonCallback", false)
    else
        ToggleCore("ResetButtonCallback", KillEvent)
    end
end)