(Yes I STILL ocassionally work on this - no clue why)
New update includes:
Now server sided
Now more efficient
No longer stores unnecessary player positions
Tween mode can be enabled and disabled easily
Tweens now display for other players
Now includes an offset
Now includes better fall threshold
Better variable naming
Prevents exploiters disabling the no fall script (as it’s now server sided)
Here’s the script: (just copy and paste it into a script inside ‘ServerScriptService’ )
--[[
Simple Dont Fall
--]]
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
-- CONFIG
local FALL_THRESHOLD_SECONDS = 2 --- How long a player must be in freefall before correcting
local PUSH_BACK_OFFSET = 3 --- Studs to push back from the last stable positions
local USE_TWEEN = true --- Set to true to tween the player back smoothly
local TWEEN_TIME = 1.2 --- Duration of the tween (in seconds) if tweening is on
-- EXPERIMENTAL
local POSITION_HISTORY_MAX = 1 --- If you want to modify the script to log the pos history before a fall event occured
local playerData = {}
local function addPosition(userId, position)
if not playerData[userId] then
playerData[userId] = { history = {}, fallStart = nil, fallRotation = nil }
end
local history = playerData[userId].history
table.insert(history, position)
if #history > POSITION_HISTORY_MAX then
table.remove(history, 1)
end
end
local function getLastStablePosition(userId)
if playerData[userId] and #playerData[userId].history > 0 then
return playerData[userId].history[#playerData[userId].history]
end
return nil
end
local function isOnSolidGround(character)
local rootPart = character:FindFirstChild("HumanoidRootPart")
if not rootPart then return false end
local rayOrigin = rootPart.Position
local rayDirection = Vector3.new(0, -5, 0)
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {character}
rayParams.FilterType = Enum.RaycastFilterType.Exclude
local result = workspace:Raycast(rayOrigin, rayDirection, rayParams)
return (result ~= nil)
end
local function teleportPlayerToStablePosition(player, character)
local stablePosition = getLastStablePosition(player.UserId)
if stablePosition and character and character:FindFirstChild("HumanoidRootPart") then
local rootPart = character.HumanoidRootPart
local currentPosition = rootPart.Position
local direction = (stablePosition - currentPosition).Unit
local targetPosition = stablePosition + direction * PUSH_BACK_OFFSET
local savedRotation = playerData[player.UserId].fallRotation or rootPart.Orientation.Y
local targetCFrame = CFrame.new(targetPosition) * CFrame.Angles(0, math.rad(savedRotation), 0)
if USE_TWEEN then
rootPart.Anchored = true
local tweenInfo = TweenInfo.new(TWEEN_TIME, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
local tween = TweenService:Create(rootPart, tweenInfo, {CFrame = targetCFrame})
tween.Completed:Connect(function()
rootPart.Anchored = false
end)
tween:Play()
else
rootPart.CFrame = targetCFrame
end
if rootPart:IsA("BasePart") then
rootPart.Velocity = Vector3.new(0, 0, 0)
end
print("Player has been teleported - " .. player.Name .. " - to previous position: " .. tostring(targetPosition) .. " with rotation: " .. savedRotation)
else
print("Uh oh :( ")
end
end
local function monitorPlayer(character, player)
local humanoid = character:WaitForChild("Humanoid")
local userId = player.UserId
playerData[userId] = { history = {}, fallStart = nil, fallRotation = nil }
humanoid.StateChanged:Connect(function(oldState, newState)
if newState == Enum.HumanoidStateType.Freefall then
if not playerData[userId].fallStart then
playerData[userId].fallStart = tick()
local rootPart = character:FindFirstChild("HumanoidRootPart")
if rootPart then
local y= rootPart.Orientation.Y
playerData[userId].fallRotation = y
end
end
elseif newState == Enum.HumanoidStateType.Landed
or newState == Enum.HumanoidStateType.Running
or newState == Enum.HumanoidStateType.RunningNoPhysics then
playerData[userId].fallStart = nil
playerData[userId].fallRotation = nil
end
end)
end
local function onCharacterAdded(character, player)
monitorPlayer(character, player)
end
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
onCharacterAdded(character, player)
end)
end)
Players.PlayerRemoving:Connect(function(player)
playerData[player.UserId] = nil
end)
RunService.Heartbeat:Connect(function()
for _, player in pairs(Players:GetPlayers()) do
local character = player.Character
if character and character:FindFirstChild("HumanoidRootPart") and character:FindFirstChild("Humanoid") then
local userId = player.UserId
local humanoid = character.Humanoid
if isOnSolidGround(character) then
addPosition(userId, character.HumanoidRootPart.Position)
end
if humanoid:GetState() == Enum.HumanoidStateType.Freefall then
local fallStart = playerData[userId] and playerData[userId].fallStart
if fallStart and (tick() - fallStart) >= FALL_THRESHOLD_SECONDS then
teleportPlayerToStablePosition(player, character)
playerData[userId].fallStart = nil
playerData[userId].fallRotation = nil
end
end
end
end
end)
Really liking this, have found two glitches which would be nice if you could fix though the first of which is double jumping (jumping twice in a row). For some reason it starts to initiate the Don’t Fall script and I float back to the location of my first jump. The second glitch is if you jump just after floating back to your previous location you start to float midair (I can provide a video if you like but you should be able to reproduce this easily enough.)
Fixed issue where the module would teleport you to the latest CFrame instead of looping through to find the safest point to move the player to.
Fixed issue where jumping multiple times can activate Don’t Fall (Hopefully)
The module now also comes with an ‘Instant Teleport’ mode that can be toggled using the following: Antifall.InstantTeleport(true) or Antifall.InstantTeleport(false)
I can already see a lot of people using this in future. Not sure if you’ve already added it, but you could also add in settings the amount of time it will take to return player back to the safe spot, pretty sure you can easily do it with the TweenService.
What is Don’t Fall?
Don’t fall creates a log of the last 5 known positions of the player prior to falling and then allows you to either tween them back or teleport them back after they have been falling for X amount of seconds.
Using the new Don’t Fall is slightly different from the previous versions.
First, we require the module.
local DontFall = require(script:WaitForChild("DontFallV2"))
Then we create our tween information
local Info = TweenInfo.new(
0.8,
Enum.EasingStyle.Sine,
Enum.EasingDirection.Out,
0,
false,
0
)
And then create a new DontFall providing it with the information required
local DF = DontFall.new(
0.5, --Time before a fall is counted (seconds)
false, --Skip tween
Info) -- Tween info
And now we have to start it by doing
DF:Start()
--or we can stop it by doing DF:Stop()
It also shouldn’t have many bugs now but if you do find one let me know and it now uses rays to find if the player is on a part so infinite fall loops shouldn’t exist now.
(Yes I STILL ocassionally work on this - no clue why)
New update includes:
Now server sided
Now more efficient
No longer stores unnecessary player positions
Tween mode can be enabled and disabled easily
Tweens now display for other players
Now includes an offset
Now includes better fall threshold
Better variable naming
Prevents exploiters disabling the no fall script (as it’s now server sided)
Here’s the script: (just copy and paste it into a script inside ‘ServerScriptService’ )
--[[
Simple Dont Fall
--]]
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")
-- CONFIG
local FALL_THRESHOLD_SECONDS = 2 --- How long a player must be in freefall before correcting
local PUSH_BACK_OFFSET = 3 --- Studs to push back from the last stable positions
local USE_TWEEN = true --- Set to true to tween the player back smoothly
local TWEEN_TIME = 1.2 --- Duration of the tween (in seconds) if tweening is on
-- EXPERIMENTAL
local POSITION_HISTORY_MAX = 1 --- If you want to modify the script to log the pos history before a fall event occured
local playerData = {}
local function addPosition(userId, position)
if not playerData[userId] then
playerData[userId] = { history = {}, fallStart = nil, fallRotation = nil }
end
local history = playerData[userId].history
table.insert(history, position)
if #history > POSITION_HISTORY_MAX then
table.remove(history, 1)
end
end
local function getLastStablePosition(userId)
if playerData[userId] and #playerData[userId].history > 0 then
return playerData[userId].history[#playerData[userId].history]
end
return nil
end
local function isOnSolidGround(character)
local rootPart = character:FindFirstChild("HumanoidRootPart")
if not rootPart then return false end
local rayOrigin = rootPart.Position
local rayDirection = Vector3.new(0, -5, 0)
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {character}
rayParams.FilterType = Enum.RaycastFilterType.Exclude
local result = workspace:Raycast(rayOrigin, rayDirection, rayParams)
return (result ~= nil)
end
local function teleportPlayerToStablePosition(player, character)
local stablePosition = getLastStablePosition(player.UserId)
if stablePosition and character and character:FindFirstChild("HumanoidRootPart") then
local rootPart = character.HumanoidRootPart
local currentPosition = rootPart.Position
local direction = (stablePosition - currentPosition).Unit
local targetPosition = stablePosition + direction * PUSH_BACK_OFFSET
local savedRotation = playerData[player.UserId].fallRotation or rootPart.Orientation.Y
local targetCFrame = CFrame.new(targetPosition) * CFrame.Angles(0, math.rad(savedRotation), 0)
if USE_TWEEN then
rootPart.Anchored = true
local tweenInfo = TweenInfo.new(TWEEN_TIME, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
local tween = TweenService:Create(rootPart, tweenInfo, {CFrame = targetCFrame})
tween.Completed:Connect(function()
rootPart.Anchored = false
end)
tween:Play()
else
rootPart.CFrame = targetCFrame
end
if rootPart:IsA("BasePart") then
rootPart.Velocity = Vector3.new(0, 0, 0)
end
print("Player has been teleported - " .. player.Name .. " - to previous position: " .. tostring(targetPosition) .. " with rotation: " .. savedRotation)
else
print("Uh oh :( ")
end
end
local function monitorPlayer(character, player)
local humanoid = character:WaitForChild("Humanoid")
local userId = player.UserId
playerData[userId] = { history = {}, fallStart = nil, fallRotation = nil }
humanoid.StateChanged:Connect(function(oldState, newState)
if newState == Enum.HumanoidStateType.Freefall then
if not playerData[userId].fallStart then
playerData[userId].fallStart = tick()
local rootPart = character:FindFirstChild("HumanoidRootPart")
if rootPart then
local y= rootPart.Orientation.Y
playerData[userId].fallRotation = y
end
end
elseif newState == Enum.HumanoidStateType.Landed
or newState == Enum.HumanoidStateType.Running
or newState == Enum.HumanoidStateType.RunningNoPhysics then
playerData[userId].fallStart = nil
playerData[userId].fallRotation = nil
end
end)
end
local function onCharacterAdded(character, player)
monitorPlayer(character, player)
end
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
onCharacterAdded(character, player)
end)
end)
Players.PlayerRemoving:Connect(function(player)
playerData[player.UserId] = nil
end)
RunService.Heartbeat:Connect(function()
for _, player in pairs(Players:GetPlayers()) do
local character = player.Character
if character and character:FindFirstChild("HumanoidRootPart") and character:FindFirstChild("Humanoid") then
local userId = player.UserId
local humanoid = character.Humanoid
if isOnSolidGround(character) then
addPosition(userId, character.HumanoidRootPart.Position)
end
if humanoid:GetState() == Enum.HumanoidStateType.Freefall then
local fallStart = playerData[userId] and playerData[userId].fallStart
if fallStart and (tick() - fallStart) >= FALL_THRESHOLD_SECONDS then
teleportPlayerToStablePosition(player, character)
playerData[userId].fallStart = nil
playerData[userId].fallRotation = nil
end
end
end
end
end)