[NOT-WORKING] Creating a client-safe way to play animations [Open-Source]

Before I say any more, this method does NOT work. However, if Roblox were to ever release a similar feature or set-up to ‘RegisterKeyframeSequence’ this might just be possible!! The idea was to send the Client a KeyframeSequence and having it be safe so the id cannot be taken, because its fake (lol)!

So heres my idea on how it works:

  1. The Client asks the Server for a specific KeyframeSequence (through a RemoteFunction)
  2. The Server takes that request, finds the KeyframeSequence, and then converts it into a table
  3. The table is returned to the Client and builds back the KeyframeSequnce using Instance.new() to recreate the KeyframeSequence, I’m not 100% sure on this part, but since the instances’ parent was not given it become invisible, sort of. (If I’m wrong please correct me.)
  4. The KeyframeSequence is turned into a hash id (or a false animation id that cannot be stolen) and the Client clears the KeyframeSequence so it cannot be used or manipulated, however I was thinking of adding a way for the server to save the table so the RemoteFunction wouldn’t take forever to respond, etc.
  5. Finally, the Client plays a safe animation that exploiters cannot go and get because its just a hash id. Unfortunately this is the part where it won’t work, because its only available in studio. Sorry.

Heres how you can recreate it:
Use this setup:
image
In the server script, use this:

ReplicatedStorage = game:GetService("ReplicatedStorage")
local SavedKeyframeSequences = {}
function ConvertKeyframeSequenceToTable(KeyframeSequence)
	local KFS_T = {}
	for i, Keyframe in pairs(KeyframeSequence:GetChildren()) do
		local Poses = {}
		for i, Pose in pairs(Keyframe:GetDescendants()) do
			table.insert(Poses, {
				Name = Pose.Name;
				CFrameNew = Pose.CFrame;
				EasingDirection = Pose.EasingDirection;
				EasingStyle = Pose.EasingStyle;
				Weight = Pose.Weight;
			})
		end
		table.insert(KFS_T, {
			BaseName = Keyframe.Name;
			BaseTime = Keyframe.Time;
			KeyframePoses = Poses;
		})
		pcall(function()
			Poses = nil
		end)
	end
	SavedKeyframeSequences[KeyframeSequence.Name] = KFS_T
	return KFS_T, pcall(function()
		KFS_T = nil
	end)
end
ReplicatedStorage.Emotes.OnServerInvoke = function(ClientPlayer)
	return ConvertKeyframeSequenceToTable(script.KeyframeSequence)
end

In the local script, use this:

task.wait(3)

local function CreateKeyframeSequence(Table)
	local KeyframeSequence = Instance.new("KeyframeSequence")
	KeyframeSequence.Parent = script
	for i = 1, #Table do
		local Keyframe = Instance.new("Keyframe")
		Keyframe.Name = Table[i].BaseName
		Keyframe.Time = Table[i].BaseTime
		Keyframe.Parent = KeyframeSequence
		for i2 = 1, #Table[i].KeyframePoses do
			local Pose = Instance.new("Pose")
			Pose.Name = Table[i].KeyframePoses[i2].Name
			Pose.CFrame = Table[i].KeyframePoses[i2].CFrameNew
			Pose.EasingDirection =  Table[i].KeyframePoses[i2].EasingDirection
			Pose.EasingStyle = Table[i].KeyframePoses[i2].EasingStyle
			Pose.Weight =  Table[i].KeyframePoses[i2].Weight
			Pose.Parent = Keyframe
		end
		Keyframe.LowerTorso.Parent = Keyframe.HumanoidRootPart
		
		Keyframe.LeftUpperLeg.Parent = Keyframe.HumanoidRootPart.LowerTorso
		Keyframe.LeftLowerLeg.Parent = Keyframe.HumanoidRootPart.LowerTorso.LeftUpperLeg
		Keyframe.LeftFoot.Parent = Keyframe.HumanoidRootPart.LowerTorso.LeftUpperLeg.LeftLowerLeg
		
		Keyframe.RightUpperLeg.Parent = Keyframe.HumanoidRootPart.LowerTorso
		Keyframe.RightLowerLeg.Parent = Keyframe.HumanoidRootPart.LowerTorso.RightUpperLeg
		Keyframe.RightFoot.Parent = Keyframe.HumanoidRootPart.LowerTorso.RightUpperLeg.RightLowerLeg
		
		Keyframe.UpperTorso.Parent = Keyframe.HumanoidRootPart.LowerTorso
		Keyframe.Head.Parent = Keyframe.HumanoidRootPart.LowerTorso.UpperTorso
		
		Keyframe.LeftUpperArm.Parent = Keyframe.HumanoidRootPart.LowerTorso.UpperTorso
		Keyframe.LeftLowerArm.Parent = Keyframe.HumanoidRootPart.LowerTorso.UpperTorso.LeftUpperArm
		Keyframe.LeftHand.Parent = Keyframe.HumanoidRootPart.LowerTorso.UpperTorso.LeftUpperArm.LeftLowerArm
		
		Keyframe.RightUpperArm.Parent = Keyframe.HumanoidRootPart.LowerTorso.UpperTorso
		Keyframe.RightLowerArm.Parent = Keyframe.HumanoidRootPart.LowerTorso.UpperTorso.RightUpperArm
		Keyframe.RightHand.Parent = Keyframe.HumanoidRootPart.LowerTorso.UpperTorso.RightUpperArm.RightLowerArm
	end
	return KeyframeSequence
end

local ActualKeyframeSequence = CreateKeyframeSequence(game:GetService("ReplicatedStorage").Emotes:InvokeServer())
local Humanoid = game.Players.LocalPlayer.Character.Humanoid
local KeyframeSequenceProvider = game:GetService("KeyframeSequenceProvider")
local Animation = Instance.new("Animation")
Animation.AnimationId = KeyframeSequenceProvider:RegisterKeyframeSequence(ActualKeyframeSequence)
local Emote = Humanoid:LoadAnimation(Animation)
Emote:Play()

I was going to use it for my emote game, but its practically worthless at the moment lol. If any of you know a better method or a way to make my idea better, etc; please let me know! I’m more than open to any of your ideas. :slight_smile:

Remember that any method that involves sending data from the server to the client has some inherent risks, as exploiters could potentially intercept and modify that data. While your method does a good job of minimizing the risks, it cannot eliminate them entirely. Always be mindful of these risks when designing and implementing networked systems in Roblox.

1 Like

I was looking through old posts and you could always jumble the table in the server every time before sending to the client and have a method for the client to un-jumble. The same risks would still be there, but way lower for most exploiters if you could script it right.