Hi everyone,
I’m currently encountering an issue with a custom guitar system I developed, modeled similarly to how piano keyboards function. Everything works perfectly in Studio, but when I try to run it in an actual Roblox game, none of the functionality executes as expected.
The only outputs I see are:
print(player.Name .. "'s Client loaded")
print(player.Name .. "'s Server loaded")
Beyond these, nothing else seems to run.
Has anyone experienced a similar issue or know why this might be happening? Any help or insight would be greatly appreciated.
Thank you!
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local HttpService = game:GetService("HttpService")
local DS = DataStoreService:GetDataStore("Datastore_test1")
local CreateSheet = ReplicatedStorage:WaitForChild("CreateSheet")
local ReceiveSheets = ReplicatedStorage:WaitForChild("ReceiveSheets")
local SetFavorite = ReplicatedStorage:WaitForChild("SetFavorite")
local DeleteSheet = ReplicatedStorage:WaitForChild("DeleteSheet")
local function getKey(player)
return "Sheets_" .. player.UserId
end
local function sendSheets(player, list)
ReceiveSheets:FireClient(player, list)
end
Players.PlayerAdded:Connect(function(player)
local key = getKey(player)
local success, saved = pcall(function()
return DS:GetAsync(key)
end)
if not success or type(saved) ~= "table" then
saved = {}
end
for _, v in ipairs(saved) do
if v.favorite == nil then v.favorite = false end
if v.timestamp == nil then v.timestamp = 0 end
end
sendSheets(player, saved)
print(player.Name .. "'s Server loaded")
end)
CreateSheet.OnServerEvent:Connect(function(player, name, sequence)
local key = getKey(player)
local success, stored = pcall(function()
return DS:GetAsync(key)
end)
if not success or type(stored) ~= "table" then
stored = {}
end
local newId = HttpService:GenerateGUID(false)
local entry = {
id = newId,
name = name,
sequence = sequence,
favorite = false,
timestamp = os.time()
}
table.insert(stored, entry)
pcall(function()
DS:SetAsync(key, stored)
end)
sendSheets(player, stored)
end)
SetFavorite.OnServerEvent:Connect(function(player, sheetId, newFav)
local key = getKey(player)
local success, stored = pcall(function()
return DS:GetAsync(key)
end)
if not success or type(stored) ~= "table" then
return
end
for _, v in ipairs(stored) do
if v.id == sheetId then
v.favorite = newFav
v.timestamp = os.time()
break
end
end
pcall(function()
DS:SetAsync(key, stored)
end)
sendSheets(player, stored)
end)
DeleteSheet.OnServerEvent:Connect(function(player, sheetId)
local key = getKey(player)
local success, stored = pcall(function()
return DS:GetAsync(key)
end)
if not success or type(stored) ~= "table" then
return
end
for idx, v in ipairs(stored) do
if v.id == sheetId then
table.remove(stored, idx)
break
end
end
pcall(function()
DS:SetAsync(key, stored)
end)
sendSheets(player, stored)
end)
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local ContextActionService = game:GetService("ContextActionService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local player = Players.LocalPlayer
local Tool = script.Parent.Parent:WaitForChild("Guitar")
local gui = Tool:WaitForChild("ScreenGui")
local frame = gui:WaitForChild("Frame")
local prototype = frame:WaitForChild("keyA")
local originalNoteSound = Tool:WaitForChild("NoteSound")
local reverbFX = originalNoteSound:WaitForChild("ReverbFX")
local soundPool = Tool:FindFirstChild("SoundPool")
local autoPlayFolder = frame:WaitForChild("autoPlay")
local sequenceBox = autoPlayFolder:WaitForChild("SequenceBox")
local playButton = autoPlayFolder:WaitForChild("PlayButton")
local loopButton = autoPlayFolder:WaitForChild("Loop")
local stopButton = autoPlayFolder:WaitForChild("Stop")
local sheetsFolder = frame:WaitForChild("sheets")
local sheetsFrame = sheetsFolder:WaitForChild("sheets")
local scrollingFrame = sheetsFrame:WaitForChild("ScrollingFrame")
local createSheetBtn = sheetsFrame:WaitForChild("createSheet")
local templateNameBox = sheetsFrame:WaitForChild("TemplateNameBox")
local CreateSheet = ReplicatedStorage:WaitForChild("CreateSheet")
local ReceiveSheets = ReplicatedStorage:WaitForChild("ReceiveSheets")
local SetFavorite = ReplicatedStorage:WaitForChild("SetFavorite")
local DeleteSheet = ReplicatedStorage:WaitForChild("DeleteSheet")
local errorText = gui:WaitForChild("error")
local succesText = gui:WaitForChild("succes")
local isWaitingForName = false
local guitarActive = false
local inputConn = nil
local unequipConn = nil
local isAutoPlaying = false
local cancelAuto = false
local loopEnabled = false
local character = player.Character or player.CharacterAdded:Wait()
local humanoid
local origWalkSpeed, origJumpPower
local defaultColors = {}
print(player.Name .. "'s Client loaded")
for _ = 1, 2 do
while true do
local info = ContextActionService:GetBoundActionInfo("RbxCameraKeypress")
if info and info.inputTypes then
ContextActionService:UnbindAction("RbxCameraKeypress")
break
else
wait()
end
end
end
local NoteMap = {
[Enum.KeyCode.Z] = { Pitch = 0.500, Reverb = { DecayTime = 1.0, WetLevel = 0.2 } },
[Enum.KeyCode.X] = { Pitch = 0.561, Reverb = { DecayTime = 1.1, WetLevel = 0.25 } },
[Enum.KeyCode.C] = { Pitch = 0.630, Reverb = { DecayTime = 1.2, WetLevel = 0.3 } },
[Enum.KeyCode.V] = { Pitch = 0.667, Reverb = { DecayTime = 1.3, WetLevel = 0.35 } },
[Enum.KeyCode.B] = { Pitch = 0.749, Reverb = { DecayTime = 1.4, WetLevel = 0.4 } },
[Enum.KeyCode.N] = { Pitch = 0.841, Reverb = { DecayTime = 1.5, WetLevel = 0.45 } },
[Enum.KeyCode.M] = { Pitch = 0.943, Reverb = { DecayTime = 1.6, WetLevel = 0.5 } },
[Enum.KeyCode.A] = { Pitch = 1.000, Reverb = { DecayTime = 1.2, WetLevel = 0.3 } },
[Enum.KeyCode.S] = { Pitch = 1.122, Reverb = { DecayTime = 1.4, WetLevel = 0.35 } },
[Enum.KeyCode.D] = { Pitch = 1.260, Reverb = { DecayTime = 1.6, WetLevel = 0.4 } },
[Enum.KeyCode.F] = { Pitch = 1.335, Reverb = { DecayTime = 1.8, WetLevel = 0.45 } },
[Enum.KeyCode.G] = { Pitch = 1.498, Reverb = { DecayTime = 2.0, WetLevel = 0.5 } },
[Enum.KeyCode.H] = { Pitch = 1.682, Reverb = { DecayTime = 2.2, WetLevel = 0.55 } },
[Enum.KeyCode.J] = { Pitch = 1.887, Reverb = { DecayTime = 2.4, WetLevel = 0.6 } },
[Enum.KeyCode.K] = { Pitch = 2.000, Reverb = { DecayTime = 2.6, WetLevel = 0.65 } },
[Enum.KeyCode.Q] = { Pitch = 2.244, Reverb = { DecayTime = 2.8, WetLevel = 0.7 } },
[Enum.KeyCode.W] = { Pitch = 2.520, Reverb = { DecayTime = 3.0, WetLevel = 0.75 } },
[Enum.KeyCode.E] = { Pitch = 2.670, Reverb = { DecayTime = 3.2, WetLevel = 0.8 } },
[Enum.KeyCode.R] = { Pitch = 2.996, Reverb = { DecayTime = 3.4, WetLevel = 0.85 } },
[Enum.KeyCode.T] = { Pitch = 3.364, Reverb = { DecayTime = 3.6, WetLevel = 0.9 } },
[Enum.KeyCode.Y] = { Pitch = 3.774, Reverb = { DecayTime = 3.8, WetLevel = 0.95 } },
[Enum.KeyCode.U] = { Pitch = 4.000, Reverb = { DecayTime = 4.0, WetLevel = 1.0 } },
[Enum.KeyCode.I] = { Pitch = 4.488, Reverb = { DecayTime = 4.2, WetLevel = 1.0 } }
}
local NoteNames = {
[Enum.KeyCode.Z] = "C3",
[Enum.KeyCode.A] = "C4",
[Enum.KeyCode.K] = "C5",
[Enum.KeyCode.U] = "C6",
[Enum.KeyCode.X] = "D3",
[Enum.KeyCode.S] = "D4",
[Enum.KeyCode.Q] = "D5",
[Enum.KeyCode.I] = "D6",
[Enum.KeyCode.C] = "E3",
[Enum.KeyCode.D] = "E4",
[Enum.KeyCode.W] = "E5",
[Enum.KeyCode.V] = "F3",
[Enum.KeyCode.F] = "F4",
[Enum.KeyCode.E] = "F5",
[Enum.KeyCode.B] = "G3",
[Enum.KeyCode.G] = "G4",
[Enum.KeyCode.R] = "G5",
[Enum.KeyCode.N] = "A3",
[Enum.KeyCode.H] = "A4",
[Enum.KeyCode.T] = "A5",
[Enum.KeyCode.M] = "B3",
[Enum.KeyCode.J] = "B4",
[Enum.KeyCode.Y] = "B5"
}
local uiListLayout = frame:WaitForChild("UIListLayout")
uiListLayout.SortOrder = Enum.SortOrder.LayoutOrder
local entries = {}
for keyCode, name in pairs(NoteNames) do
entries[#entries + 1] = { key = keyCode, name = name }
end
table.sort(entries, function(a, b)
return a.name < b.name
end)
for index, entry in ipairs(entries) do
local keyCode = entry.key
local clone = prototype:Clone()
clone.Name = "key" .. keyCode.Name
clone.Text = keyCode.Name .. " - " .. entry.name
clone.LayoutOrder = index
clone.Parent = frame
defaultColors[keyCode] = clone.TextColor3
local pitchBox = clone:FindFirstChild("editFrame"):FindFirstChild("pitch")
local reverbBox = clone:FindFirstChild("editFrame"):FindFirstChild("reverb")
if pitchBox and NoteMap[keyCode] then
pitchBox.Text = "Pitch: " .. tostring(NoteMap[keyCode].Pitch)
end
if reverbBox and NoteMap[keyCode] and NoteMap[keyCode].Reverb then
local rv = NoteMap[keyCode].Reverb
reverbBox.Text = "Reverb: " .. string.format("%.2f,%.2f", rv.DecayTime, rv.WetLevel)
end
if pitchBox then
pitchBox.FocusLost:Connect(function(enterPressed)
local newPitch = tonumber(pitchBox.Text)
if newPitch and NoteMap[keyCode] then
NoteMap[keyCode].Pitch = newPitch
else
pitchBox.Text = "Pitch: " .. tostring(NoteMap[keyCode].Pitch)
end
end)
end
if reverbBox then
reverbBox.FocusLost:Connect(function(enterPressed)
local text = reverbBox.Text
local decay, wet = text:match("^%s*(%d*%.?%d+)%s*,%s*(%d*%.?%d+)%s*$")
decay = tonumber(decay)
wet = tonumber(wet)
if decay and wet and NoteMap[keyCode] then
if decay > 20 then decay = 20 end
if wet > 20 then wet = 20 end
NoteMap[keyCode].Reverb.DecayTime = decay
NoteMap[keyCode].Reverb.WetLevel = wet
reverbBox.Text = "Reverb: " .. string.format("%.2f,%.2f", decay, wet)
else
local old = NoteMap[keyCode].Reverb
reverbBox.Text = "Reverb: " .. string.format("%.2f,%.2f", old.DecayTime, old.WetLevel)
end
end)
end
end
prototype:Destroy()
loopButton.BackgroundColor3 = Color3.new(1, 0, 0)
local function setMovementLocked(lock)
if humanoid then
humanoid.UseJumpPower = true
if lock then
humanoid.WalkSpeed = 0
humanoid.JumpPower = 0
else
humanoid.WalkSpeed = origWalkSpeed
humanoid.JumpPower = origJumpPower
end
end
end
local function playNoteSound(noteInfo)
local mainClone = originalNoteSound:Clone()
mainClone.Parent = soundPool
mainClone.Pitch = noteInfo.Pitch or 1
local rv = mainClone:FindFirstChild("ReverbFX")
if rv and noteInfo.Reverb then
rv.DecayTime = noteInfo.Reverb.DecayTime or rv.DecayTime
rv.WetLevel = noteInfo.Reverb.WetLevel or rv.WetLevel
end
local silentTemplate = Tool:FindFirstChild("SilentHold")
local silentClone = nil
if silentTemplate then
silentClone = silentTemplate:Clone()
silentClone.Parent = soundPool
silentClone.Volume = 0
local rv2 = silentClone:FindFirstChild("ReverbFX")
if rv2 then
rv2.DecayTime = noteInfo.Reverb and noteInfo.Reverb.DecayTime or rv2.DecayTime
rv2.WetLevel = noteInfo.Reverb and noteInfo.Reverb.WetLevel or rv2.WetLevel
end
end
mainClone:Play()
if silentClone then
mainClone.Ended:Connect(function()
silentClone:Play()
mainClone:Destroy()
local decay = (noteInfo.Reverb and noteInfo.Reverb.DecayTime) or (rv and rv.DecayTime) or 0
spawn(function()
task.wait(decay + 0.1)
if silentClone and silentClone.Parent then
silentClone:Destroy()
end
end)
end)
else
local totalWait =
(mainClone.TimeLength or 0)
+ ((noteInfo.Reverb and noteInfo.Reverb.DecayTime) or (rv and rv.DecayTime) or 0)
+ 1
spawn(function()
task.wait(totalWait)
if mainClone and mainClone.Parent then
mainClone:Destroy()
end
end)
end
end
local function flashLabel(keyCode, color)
local label = frame:FindFirstChild("key" .. keyCode.Name)
if not label then return end
label.TextColor3 = color
task.delay(0.2, function()
local defaultColor = defaultColors[keyCode]
if defaultColor then
label.TextColor3 = defaultColor
end
end)
end
local function onInputBegan(input, gameProcessed)
if gameProcessed then return end
if input.KeyCode == Enum.KeyCode.Backspace then
guitarActive = not guitarActive
if guitarActive then
setMovementLocked(true)
else
setMovementLocked(false)
end
return
end
if isAutoPlaying then return end
if not guitarActive then return end
local noteInfo = NoteMap[input.KeyCode]
if noteInfo then
playNoteSound(noteInfo)
flashLabel(input.KeyCode, Color3.new(0, 1, 0))
end
end
local function parseSequence(sequenceText)
local actions = {}
local i = 1
while i <= #sequenceText do
local ch = sequenceText:sub(i, i)
if ch == "[" then
local close = sequenceText:find("]", i + 1, true)
if close then
local inside = sequenceText:sub(i + 1, close - 1)
local numStr = inside:match("%-?%d+%.?%d*")
local n = tonumber(numStr)
if n then
actions[#actions + 1] = { type = "delay", time = math.abs(n) }
end
i = close + 1
else
i = i + 1
end
elseif ch:match("%a") then
local upper = ch:upper()
local success, enumKey = pcall(function() return Enum.KeyCode[upper] end)
if success and NoteMap[enumKey] then
actions[#actions + 1] = { type = "note", key = enumKey }
end
i = i + 1
else
i = i + 1
end
end
return actions
end
local function playSequence(actions)
isAutoPlaying = true
cancelAuto = false
for _, action in ipairs(actions) do
if cancelAuto then break end
if action.type == "delay" then
task.wait(action.time)
elseif action.type == "note" then
local noteInfo = NoteMap[action.key]
if noteInfo then
playNoteSound(noteInfo)
flashLabel(action.key, Color3.new(1, 0, 0))
end
task.wait(0.15)
end
end
isAutoPlaying = false
cancelAuto = false
end
loopButton.MouseButton1Click:Connect(function()
loopEnabled = not loopEnabled
if loopEnabled then
loopButton.BackgroundColor3 = Color3.new(0, 1, 0)
else
loopButton.BackgroundColor3 = Color3.new(1, 0, 0)
end
end)
stopButton.MouseButton1Click:Connect(function()
if isAutoPlaying then cancelAuto = true end
end)
playButton.MouseButton1Click:Connect(function()
if not guitarActive or isAutoPlaying then return end
local rawText = sequenceBox.Text or ""
local actions = parseSequence(rawText)
if #actions == 0 then return end
spawn(function()
if loopEnabled then
while loopEnabled and not cancelAuto do
playSequence(actions)
end
else
playSequence(actions)
end
end)
end)
local function onUnequip()
if inputConn then
inputConn:Disconnect()
inputConn = nil
end
if isAutoPlaying then cancelAuto = true end
setMovementLocked(false)
gui.Parent = script.Parent
guitarActive = false
if unequipConn then
unequipConn:Disconnect()
unequipConn = nil
end
end
local function onEquip()
humanoid = character:WaitForChild("Humanoid")
origWalkSpeed = humanoid.WalkSpeed
origJumpPower = humanoid.JumpPower
guitarActive = true
setMovementLocked(true)
inputConn = UserInputService.InputBegan:Connect(onInputBegan)
gui.Parent = player.PlayerGui
unequipConn = Tool.Unequipped:Connect(onUnequip)
end
Tool.Equipped:Connect(onEquip)
Tool.Unequipped:Connect(onUnequip)
local sheetDataList = {}
local function sortSheets(list)
table.sort(list, function(a, b)
if a.favorite and not b.favorite then
return true
elseif b.favorite and not a.favorite then
return false
else
return a.timestamp > b.timestamp
end
end)
end
local function clearScrollingFrame()
for _, child in ipairs(scrollingFrame:GetChildren()) do
if child:IsA("TextButton") then
child:Destroy()
end
end
end
local function populateSheets(list)
clearScrollingFrame()
sheetDataList = list
sortSheets(sheetDataList)
for _, data in ipairs(sheetDataList) do
local sheetBtn = script:WaitForChild("template"):Clone()
local id = data.id or ""
local nameText = data.name or ""
sheetBtn.Name = "SheetBtn_" .. id
sheetBtn.name.Text = nameText
if data.favorite then
sheetBtn.BackgroundColor3 = Color3.new(1, 1, 0)
else
sheetBtn.BackgroundColor3 = Color3.new(0, 1, 0)
end
sheetBtn.LayoutOrder = 0
local pendingDelete = false
local isHeld = false
local favoriteToggled = false
local function onInputBeganBtn(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
isHeld = true
favoriteToggled = false
task.spawn(function()
task.wait(0.5)
if isHeld and not pendingDelete and not favoriteToggled then
favoriteToggled = true
SetFavorite:FireServer(id, not data.favorite)
if not data.favorite then
succesText.Visible = true
succesText.Text = "Favorited '" .. nameText .. "'"
else
errorText.Visible = true
errorText.Text = "Unfavorited '" .. nameText .. "'"
end
task.delay(2, function()
errorText.Visible = false
succesText.Visible = false
errorText.Text = ""
succesText.Text = ""
end)
sheetBtn.BackgroundColor3 = Color3.new(1, 1, 0)
end
end)
elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
if not pendingDelete then
pendingDelete = true
sheetBtn.BackgroundColor3 = Color3.new(1, 0, 0)
else
pendingDelete = false
if data.favorite then
sheetBtn.BackgroundColor3 = Color3.new(1, 1, 0)
else
sheetBtn.BackgroundColor3 = Color3.new(0, 1, 0)
end
end
end
end
local function onInputEndedBtn(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
isHeld = false
if pendingDelete then
DeleteSheet:FireServer(id)
errorText.Visible = true
errorText.Text = "Deleted '" .. nameText .. "'"
task.delay(2, function()
errorText.Visible = false
errorText.Text = ""
end)
elseif not favoriteToggled then
sequenceBox.Text = data.sequence or ""
end
end
end
sheetBtn.InputBegan:Connect(onInputBeganBtn)
sheetBtn.InputEnded:Connect(onInputEndedBtn)
sheetBtn.Parent = scrollingFrame
end
local layout = scrollingFrame:FindFirstChildOfClass("UIListLayout")
if layout then
scrollingFrame.CanvasSize = UDim2.new(0, 0, 0, layout.AbsoluteContentSize.Y)
end
end
ReceiveSheets.OnClientEvent:Connect(function(list)
populateSheets(list)
end)
createSheetBtn.MouseButton1Click:Connect(function()
if not isWaitingForName then
isWaitingForName = true
templateNameBox.Visible = true
return
end
local rawName = templateNameBox.Text or ""
local nameTrimmed = rawName:match("^%s*(.-)%s*$")
if nameTrimmed == "" then
templateNameBox.TextColor3 = Color3.new(1, 0, 0)
task.delay(0.5, function()
templateNameBox.TextColor3 = Color3.new(1, 1, 1)
end)
errorText.Visible = true
errorText.Text = "Your name can't be blank."
task.delay(2, function()
errorText.Visible = false
errorText.Text = ""
end)
return
end
for _, data in ipairs(sheetDataList) do
if data.name == nameTrimmed then
errorText.Visible = true
errorText.Text = "A sheet named '" .. nameTrimmed .. "' already exists."
task.delay(2, function()
errorText.Visible = false
errorText.Text = ""
end)
return
end
end
local rawSequence = sequenceBox.Text or ""
if rawSequence == "" then
sequenceBox.TextColor3 = Color3.new(1, 0, 0)
task.delay(0.5, function()
sequenceBox.TextColor3 = Color3.new(1, 1, 1)
end)
errorText.Visible = true
errorText.Text = "Your sheet can't be empty."
task.delay(2, function()
errorText.Visible = false
errorText.Text = ""
end)
return
end
CreateSheet:FireServer(nameTrimmed, rawSequence)
templateNameBox.Text = ""
templateNameBox.Visible = false
isWaitingForName = false
end)