I am trying to make a door system. However, sometimes, when clicking the door, it breaks and either the cooldown stops functioning, or the door starts rotating in the wrong way, or both. I believe the cause of this issue is because there is a tiny period where the attributes between the server and client are not connected, breaking it; however, that is just a guess. I truly have no idea.
(DOORMANAGER - REPLICATEDFIRST - CLIENT SCRIPT)
DoorEvent.OnClientEvent:Connect(function(door)
local subtype = door:GetAttribute("Subtype")
local open = door:GetAttribute("Open")
local cooldown = door:GetAttribute("Cooldown")
-- find subtype module script
local doorType = script.DoorTypes:FindFirstChild(subtype)
if not doorType then
warn("No door subtype found.")
return
else
doorType = require(script.DoorTypes[subtype])
end
-- play door functionality
if not cooldown then
if doorType.moveType == "Hinge" then
ActivateDoorCDEvent:FireServer(door, true)
local Hinge = door.Door.Hinge
local rotation = doorType.Rotation
if not open then
rotation = -rotation
end
local DoorTween = TweenService:Create(Hinge, doorType.tweenInfo, {CFrame = Hinge.CFrame * CFrame.Angles(0, math.rad(rotation), 0)})
DoorTween:Play()
-- wait for door to open and then remove cooldown
DoorTween.Completed:Wait()
if door:GetAttribute("FinalizingPlayer") == Client.UserId then -- makes sure only one player fires the server
FinalizeDoorEvent:FireServer(door, "Hinge", Hinge:GetPivot())
end
end
end
end)
(OPEN DOOR SCRIPT - SERVER SCRIPT SERVICE - SERVER SIDE)
-- doors
finalizeDoorEvent.OnServerEvent:Connect(function(player, door, specification, rotation)
if not door:FindFirstChild("Base", true) then return end
if specification == "Hinge" and door then
local Hinge = door.Door.Hinge
Hinge:PivotTo(rotation)
door:SetAttribute("Open", not door:GetAttribute("Open"))
door:SetAttribute("Cooldown", false)
end
end)
activateDoorCDEvent.OnServerEvent:Connect(function(player, door, on)
if not door:FindFirstChild("Base", true) then return end
door:SetAttribute("Cooldown", on)
if on then
door:SetAttribute("FinalizingPlayer", player.UserId)
end
end)
For further clarification, the door tweening is being fired to all clients. Which is why it needs to check the userId. Also, it takes a lot of effort to break them sometimes, or not at all, in the attached screenshot
Do synchronization between the client and server by adding proper state management and cooldown handling. Instead of relying solely on attributes, use server-side validation to control the door’s state and cooldown, and communicate updates to clients via RemoteEvents. (e.g. add a server-side check to prevent multiple clients from triggering the door simultaneously and ensure the cooldown is enforced server-side before allowing the door to activate)
I’m having a little bit of trouble understanding, could you simplify what you mean? Like for example, what do you mean by “use server-side validation”?
When I say “use server-side validation,” I mean that the server should be the one in control of the door’s state (whether it’s open or closed) and its cooldown. Right now, it seems like the client is making decisions about the door (like playing the tween and checking the cooldown), and this can cause issues if the client and server are out of sync.
Server-side:
-- Server controls the door
activateDoorCDEvent.OnServerEvent:Connect(function(player, door)
if not door:FindFirstChild("Base", true) then return end
-- Check if the door is on cooldown
if door:GetAttribute("Cooldown") then return end
-- Start cooldown
door:SetAttribute("Cooldown", true)
door:SetAttribute("FinalizingPlayer", player.UserId)
-- Tell the client to play the tween
DoorEvent:FireAllClients(door, "Open") -- "Open" or "Close" based on the door’s state
-- Wait for the cooldown to finish
task.wait(5) -- Adjust this to your cooldown time
-- End cooldown
door:SetAttribute("Cooldown", false)
end)
Client-side:
-- Client just listens and plays the tween
DoorEvent.OnClientEvent:Connect(function(door, action)
local subtype = door:GetAttribute("Subtype")
local doorType = script.DoorTypes:FindFirstChild(subtype)
if not doorType then return end
doorType = require(script.DoorTypes[subtype])
if doorType.moveType == "Hinge" then
local Hinge = door.Door.Hinge
local rotation = doorType.Rotation
if action == "Open" then
rotation = -rotation
end
local DoorTween = TweenService:Create(Hinge, doorType.tweenInfo, {CFrame = Hinge.CFrame * CFrame.Angles(0, math.rad(rotation), 0)})
DoorTween:Play()
end
end)
Had to modify a little bit, but GOD do I feel stupid… I have no idea in the first place as to why I had it function around the client instead of the server…