Problems with events

So I’m working on some sort of watch tool and what I’m trying to do is when the tool gets activated, I make the watch fire a remote to the server to open the watch cover in the public, so everyone can see it.

Although, I have had a lot of issues with OnServerEvent, and not a single with OnClientEvent. I have 2 types of scripts. The local script & server script.

Local Script:

-- // SERVICES
local Lighting = game:GetService("Lighting")
local Players = game:GetService("Players")

-- // VARIABLES
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid")
local ViewingAnimation = Instance.new("Animation")
ViewingAnimation.AnimationId = "rbxassetid://4663729666"
local Handle = script.Parent:WaitForChild("Handle")
local Load = Humanoid:LoadAnimation(ViewingAnimation)
local Thought = Handle:WaitForChild("Gui")
local Label = Thought:WaitForChild("TextLabel")
local Tool = script.Parent

local WatchRemote = script.Parent:WaitForChild("WatchRemote")

local Toggled = false

-- // ANIMATIONS
TrackLoad = Humanoid:LoadAnimation(ViewingAnimation)

-- // MAIN
Tool.Unequipped:Connect(function()
	if Toggled then
		Toggled = false
		Label.Visible = false
		if Load.IsPlaying then Load:Stop() end
	end
end)

Tool.Activated:Connect(function()
	if not Toggled then
		Toggled = true
		Label.Visible = true
		Load:Play()
		WatchRemote:FireServer("Open")
		while Toggled do
			wait(.1)
		  Label.Text = "It is " .. Lighting.TimeOfDay:sub(1, 5)
		end
	elseif Toggled then
		Toggled = false
		Label.Visible = false
		Load:Stop()
		WatchRemote:FireServer("Close")
	end
end)

Server Script:

local Remote = script.Parent:WaitForChild("WatchRemote")
local TweenService = game:GetService("TweenService")
local Cover = script.Parent:WaitForChild("Cover")
local Open = false

function TweenModel(Obj, Info, Pos, Wait)
	local OldCFrame = Obj:FindFirstChild(Obj.Name)
	if OldCFrame and OldCFrame:IsA('CFrameValue') then OldCFrame:Destroy() end	
	local CFrameValue = Instance.new("CFrameValue")
	CFrameValue.Name = Obj.Name
	CFrameValue.Parent = Obj
	CFrameValue.Value = Obj:GetPrimaryPartCFrame()
	CFrameValue:GetPropertyChangedSignal("Value"):Connect(function() Obj:SetPrimaryPartCFrame(CFrameValue.Value) end)
	local TweenInf = TweenInfo.new(Info.Time,Info.Style,Info.Direction,Info.Repeat or 0,Info.Reverse or false)
	local Tween = TweenService:Create(CFrameValue, TweenInf, {Value = Pos})
	Tween:Play()
	Tween.Completed:Connect(function()
		if Wait then
			Wait()
		end
		if CFrameValue ~= nil then
			CFrameValue:Destroy()
		end
	end)
end

function OpenCover()
	local Info = {Time = 1.3, Style = Enum.EasingStyle.Linear, Direction = Enum.EasingDirection.Out}
	local Pos = Cover.PrimaryPart.CFrame * CFrame.Angles(math.rad(-45), 0, 0)
	TweenModel(Cover, Info, Pos)
end

function CloseCover()
	local Info = {Time = 1.3, Style = Enum.EasingStyle.Linear, Direction = Enum.EasingDirection.Out}
	local Pos = Cover.PrimaryPart.CFrame * CFrame.Angles(math.rad(45), 0, 0)
	TweenModel(Cover, Info, Pos)
end

Remote.OnServerEvent:Connect(function(Player, Action)
	if Action == "Open" then
		coroutine.wrap(OpenCover)()
	elseif Action == "Close" then
		coroutine.wrap(CloseCover)()
	end
end)

script.Parent.Unequipped:Connect(function()
	coroutine.wrap(CloseCover)()
end)

Keep in case these scripts are inside the tool, so is the event.

  • I’m still learning events.

What sort of issues are you having with the Server Script(s)?

Well, It feels like as if the “Action” isn’t being read, and I’ve been having issues with this for some time now.

Have you tried printing the value of Action if that’s where you think the problem might be?

Use a print statement and then check it is printing correctly on the server.

I added prints, it seems to output fine but the opening & close feature doesn’t work.

Remote.OnServerEvent:Connect(function(Player, Action)
	print("event fired")
	if Action == "Open" then
		print("opening cover")
		coroutine.wrap(OpenCover)()
	elseif Action == "Close" then
		print("closing cover")
		coroutine.wrap(CloseCover)()
	end
end)

image

I just tested your code on some parts in the Workspace and it worked fine for me. Could you elaborate on the issues you’ve been experiencing and maybe include a place file with the affected tool in it?

There’s no issues with the tweening part as far as I can tell, provided the parts are Anchored.

If the parts are Unanchored, you might be better off manipulating welds or constraints because if the object moves at all during the animation it may end up looking weird.

There aren’t any issues, it’s just like the tween won’t play.

WatchTest.rbxl (29.1 KB)

It’s because the model you are trying to CFrame is unanchored and welded to the rest of the tool. You’re trying to move it but the weld keeps bringing it back. You want to manipulate either the C0 or C1 property of the Weld instead of the model’s PrimaryPart CFrame.

You’ll need to make sure any welds relating to the lid and its design are inside the Cover model, and you’ll want a single weld between the hinge pieces - this weld is the one you want to manipulate.

Aight, I’ll see what I can do.

So something like this?

image

Also I’m not good with tweening C0s or C1s.

No worries. I’ve updated your place file to have the correct welds and updated the code to tween the weld’s C0 instead of the model CFrame.

Take a look at the hierarchy, which parts are welded to which, and the code that I edited, and hopefully you’ll be able to understand why I’ve set it up like that.

Place file: WatchTest_fixed.rbxl (30.8 KB)

For anyone who doesn't want to open the place file, here's the changes in steps:
  • The welds from Handle to HingeCover, Lid and LidDesign were removed.

  • LidDesign and Lid were then welded to HingeCover.

  • HingeCover was then welded to Hinge. The C1 of the weld offset it so the axis of the weld sits at the hinge point.

  • The tween then adjusts the C0 of the HingeCover-to-Hinge weld to rotate it by the amount that kanchoplets wanted in the original script - 45 degrees. It had to switch from X to Z axis due to the weld orientation. The signs also flipped for open and closed.


Here is the updated hierarchy of the parts and welds:

In red is the Weld we are manipulating in the animation:
image


The local script and remote event did not need editing.

Here is the updated server script:
local Remote = script.Parent:WaitForChild("WatchRemote")
local TweenService = game:GetService("TweenService")
local Cover = script.Parent:WaitForChild("Cover")
local Open = false

function TweenWeld(Weld, Info, Pos, Wait)
	local OldCFrame = Weld:FindFirstChild(Weld.Name)
	if OldCFrame and OldCFrame:IsA('CFrameValue') then OldCFrame:Destroy() end	
	local CFrameValue = Instance.new("CFrameValue")
	CFrameValue.Name = Weld.Name
	CFrameValue.Parent = Weld
	CFrameValue.Value = Weld.C0
	CFrameValue:GetPropertyChangedSignal("Value"):Connect(function() Weld.C0 = CFrameValue.Value end)
	local TweenInf = TweenInfo.new(Info.Time,Info.Style,Info.Direction,Info.Repeat or 0,Info.Reverse or false)
	local Tween = TweenService:Create(CFrameValue, TweenInf, {Value = Pos})
	Tween:Play()
	Tween.Completed:Connect(function()
		if Wait then
			Wait()
		end
		if CFrameValue ~= nil then
			CFrameValue:Destroy()
		end
	end)
end

function OpenCover()
	local Info = {Time = 1.3, Style = Enum.EasingStyle.Linear, Direction = Enum.EasingDirection.Out}
	local Pos = Cover.HingeCover.Hinge.C0 * CFrame.Angles(0, 0, math.rad(45))
	TweenWeld(Cover.HingeCover.Hinge, Info, Pos)
end

function CloseCover()
	local Info = {Time = 1.3, Style = Enum.EasingStyle.Linear, Direction = Enum.EasingDirection.Out}
	local Pos = Cover.HingeCover.Hinge.C0 * CFrame.Angles(0, 0, math.rad(-45))
	TweenWeld(Cover.HingeCover.Hinge, Info, Pos)
end

Remote.OnServerEvent:Connect(function(Player, Action)
	print("event fired")
	if Action == "Open" then
		print("opening cover")
		coroutine.wrap(OpenCover)()
	elseif Action == "Close" then
		print("closing cover")
		coroutine.wrap(CloseCover)()
	end
end)

script.Parent.Unequipped:Connect(function()
	coroutine.wrap(CloseCover)()
end)
1 Like

Thanks! This might possibly help me in the future as well.

Although, is it possible to make it return to it’s default position? Since when you spam it, it sometimes goes over.

Yeah you can record the tween into a variable outside the function instead of using a local variable, and cancel it if another click comes in (or ignore the click if it’s still running by using a debounce).

It depends what you want the behaviour to be.

So something like this?

-- // SERVICES
local Lighting = game:GetService("Lighting")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")

-- // VARIABLES
local Player = Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid")
local ViewingAnimation = Instance.new("Animation")
ViewingAnimation.AnimationId = "rbxassetid://4663729666"
local Handle = script.Parent:WaitForChild("Handle")
local Load = Humanoid:LoadAnimation(ViewingAnimation)
local Thought = Handle:WaitForChild("Gui")
local Label = Thought:WaitForChild("TextLabel")
local Tool = script.Parent

local WatchRemote = script.Parent:WaitForChild("WatchRemote")

local Toggled = false
local Debounce = false

-- // ANIMATIONS
TrackLoad = Humanoid:LoadAnimation(ViewingAnimation)

-- // MAIN
Tool.Unequipped:Connect(function()
	if Toggled and not Debounce then
		Debounce = true
		Toggled = false
		Label.Visible = false
		if Load.IsPlaying then Load:Stop() end
	end
end)

Tool.Activated:Connect(function()
	if not Toggled and not Debounce then
		Toggled = true
		Label.Visible = true
		Debounce = true
		Load:Play()
		WatchRemote:FireServer("Open")
		while Toggled do
		  Label.Text = "It is " .. Lighting.TimeOfDay:sub(1, 5)
		  RunService.Heartbeat:Wait()
		end
	elseif Toggled and not Debounce then
		Toggled = false
		Label.Visible = false
		Debounce = true
		Load:Stop()
		WatchRemote:FireServer("Close")
	end
end)

RunService.Stepped:Connect(function()
	if Debounce then
		wait(.6)
		Debounce = false
	end
end)

If so, for some reason the debounce doesn’t work sometimes.

You probably want a debounce on the server rather than the client. So the server just rejects the input from the client until it’s ready to do a new animation.

Unfortunately I’m out the house now so if you’re still stuck by this evening I’ll show you how to set that up within the tween function you’ve got.

Alright, I’ll see what I can do.

Still can’t find the solution.

local Remote = script.Parent:WaitForChild("WatchRemote")
local TweenService = game:GetService("TweenService")
local Cover = script.Parent:WaitForChild("Cover")
local Open = false

local OriginalC0 = Cover:WaitForChild( 'HingeCover' ):WaitForChild( 'Hinge' ).C0
local CurrentTween = nil

function TweenWeld(Weld, Info, Pos, Wait)
	local OldCFrame = Weld:FindFirstChild(Weld.Name)
	if OldCFrame and OldCFrame:IsA('CFrameValue') then OldCFrame:Destroy() end	
	local CFrameValue = Instance.new("CFrameValue")
	CFrameValue.Name = Weld.Name
	CFrameValue.Parent = Weld
	CFrameValue.Value = Weld.C0
	CFrameValue:GetPropertyChangedSignal("Value"):Connect(function() Weld.C0 = CFrameValue.Value end)
	local TweenInf = TweenInfo.new(Info.Time,Info.Style,Info.Direction,Info.Repeat or 0,Info.Reverse or false)
	local Tween = TweenService:Create(CFrameValue, TweenInf, {Value = Pos})
	Tween:Play()
	Tween.Completed:Connect(function( tweenStatus )
            if tweenStatus == Enum.TweenStatus.Completed then
		        if Wait then
			        Wait()
		        end
		        if CFrameValue ~= nil then
			        CFrameValue:Destroy()
		        end
            end
	end)
    return Tween
end

function OpenCover()
    if CurrentTween and CurrentTween.PlaybackState == Enum.PlaybackState.Playing then
        return
    end
	local Info = {Time = 1.3, Style = Enum.EasingStyle.Linear, Direction = Enum.EasingDirection.Out}
	local Pos = OriginalC0 * CFrame.Angles(0, 0, math.rad(45))
	CurrentTween = TweenWeld(Cover.HingeCover.Hinge, Info, Pos, function()
        Cover.OpenStatus.Value = true
    end)
end

function CloseCover()
    if CurrentTween and CurrentTween.PlaybackState == Enum.PlaybackState.Playing then
        return
    end
	local Info = {Time = 1.3, Style = Enum.EasingStyle.Linear, Direction = Enum.EasingDirection.Out}
	local Pos = OriginalC0
	CurrentTween = TweenWeld(Cover.HingeCover.Hinge, Info, Pos, function()
        Cover.OpenStatus.Value = false
    end)
end

local Bool = Instance.new('BoolValue', Cover)
Bool.Name = 'OpenStatus'

Remote.OnServerEvent:Connect(function(Player, Action)
	print("event fired")
	if Action == "Open" then
		print("opening cover")
		coroutine.wrap(OpenCover)()
	elseif Action == "Close" then
		print("closing cover")
		coroutine.wrap(CloseCover)()
	end
end)

script.Parent.Unequipped:Connect(function()
	coroutine.wrap(CloseCover)()
end)

And on the client, instead of using Toggled to determine if the watch should be opened or closed, use Tool.Cover.OpenStatus.Value in the condition instead.

I wrote this on my phone so let me know if there are any issues.

Essentially it blocks the request to open or close if there is currently an animation going on for the cover. The change on the client and the addition of OpenStatus is to prevent the client’s Toggled value becoming out of sync with the actual state of the cover due to blocked requests for it to open/close.

Alright, hopefully this should work once I do some tweaking, and fixes if needed.