Hey, currently I’m trying to make an afk system, which makes the player’s character material to forcefield, and gives the player a forcefield when the player has clicked away from the screen OR if the player has not been doing any inputs on their device for 60 seconds+. Ive done most of it, when the player clicks away from the roblox tab it works and all, but basically when i do inputs it doesnt reset back the timer (should go back to 60).
this is my script, i can show in-game aswell:
local ReplicatedStorage = game:GetService("ReplicatedStorage").Events
local UserInputService = game:GetService("UserInputService")
local Focused = ReplicatedStorage:WaitForChild("Focused")
local UnFocused = ReplicatedStorage:WaitForChild("Unfocused")
local AFKTimeThreshold = 60 -- in seconds
local forceFieldCreated = {} -- Track if force field is already created for each player
local lastActivityTime = {} -- Track the last activity time for each player
local function CreateForceField(player)
if not forceFieldCreated[player] then
if not player.Character:FindFirstChild("ForceField") then
local forceField = Instance.new("ForceField")
forceField.Visible = true
forceField.Parent = player.Character
for _,v in ipairs(player.Character:GetDescendants()) do
if v:IsA("MeshPart") then
v.Material = Enum.Material.ForceField
end
end
forceFieldCreated[player] = true
print(player.Name .. " force field created at: ", tick())
end
end
end
local function RemoveForceField(player)
if forceFieldCreated[player] then
local forceField = player.Character:FindFirstChild("ForceField")
if forceField then
forceField:Destroy()
for _,v in ipairs(player.Character:GetDescendants()) do
if v:IsA("MeshPart") then
v.Material = Enum.Material.Plastic
end
end
forceFieldCreated[player] = nil
print(player.Name .. " force field removed at: ", tick())
end
end
end
local function IsPlayerAFK(player)
return (tick() - lastActivityTime[player]) > AFKTimeThreshold
end
Focused.OnServerEvent:Connect(function(Player)
RemoveForceField(Player)
end)
UnFocused.OnServerEvent:Connect(function(Player)
CreateForceField(Player)
end)
game.Players.PlayerAdded:Connect(function(player)
lastActivityTime[player] = tick() -- Initialize last activity time for each player
while wait(1) do
local timeLeft = math.max(0, AFKTimeThreshold - (tick() - lastActivityTime[player]))
print(player.Name .. " timer at: " .. timeLeft .. " seconds")
if IsPlayerAFK(player) then
print(player.Name .. " is AFK")
CreateForceField(player)
else
print(player.Name .. " is not AFK")
RemoveForceField(player)
end
end
end)
local function UpdateLastActivityTime(player)
lastActivityTime[player] = tick()
end
UserInputService.InputBegan:Connect(function(input)
local player = game.Players:GetPlayerFromCharacter(input.UserInputType)
if player then
UpdateLastActivityTime(player)
print(player.Name .. " input detected at: ", tick())
end
end)
UserInputService.InputChanged:Connect(function(input)
local player = game.Players:GetPlayerFromCharacter(input.UserInputType)
if player then
UpdateLastActivityTime(player)
print(player.Name .. " input changed at: ", tick())
end
end)
When you get the player at the bottom, you attempt to get their character from the user input type. User input type is for example, keyboard, touch, etc. Instead, just set the player to game.Players.localplayer.
It seems you have mixed up server and client scripts?
Also this line makes no sense, UserInputType is not a character…
local player = game.Players:GetPlayerFromCharacter(input.UserInputType)
Also your solution is way too complicated imho. Allow me to simplify it.
I would say all the local script needs to do is to fire server when there is an input, and make the server start a timer.
local ActiveEvent = game.ReplicatedStorage:WaitForChild('ActiveEvent')
UserInputService.InputBegan:Connect(function(input)
ActiveEvent:FireServer()
end)
Server Script will keep track of active players:
local ActiveEvent = Instance.new("RemoteEvent")
ActiveEvent.Name = "ActiveEvent"
ActiveEvent.Parent = game.RepicatedStorage
local TimeOutTicks = {}
ActiveEvent.OnServerEvent:Connect(function(player)
RemoveForceField(player)
if not TimeOutTicks[player] then
TimeOutTicks[player] = 0
end
TimeOutTicks[player] += 1
local thisTick = TimeOutTicks[player]
task.wait(AFKTimeThreshold)
if player.Parent and thisTick == TimeOutTicks[player] then
CreateForceField(player)
end
end)
Be aware though. This script is super exploitable, I would also add special function that removes force field when server detects character movement or any other player input based remote event.
I guess that would be very recommended to add this as an extra. It could also be the alternative to my solution. It really depends on the game.
Can the player still affect the game without moving their character? If not, then local script could be scrapped, and the server could rely on avatar movement only.
sorry for not replying earlier, was @ school. this is my game setup, ill try to make it compatible with the things you provided me with, but im pretty new to scripting, i doubt ill be able to make it myself:
this is the script I provided, which is a server script:
and this is a local script from StarterPlayerScripts:
actually i kind of figured it out by myself surprisingly, and it works perfectly on my part! can someone check it out and let me know how i could improve the script, perchance?
local ReplicatedStorage = game:GetService("ReplicatedStorage").Events
local ActiveEvent = ReplicatedStorage:WaitForChild("ActiveEvent")
local Focused = ReplicatedStorage:WaitForChild("Focused")
local UnFocused = ReplicatedStorage:WaitForChild("Unfocused")
local AFKTimeThreshold = 60 -- in seconds
local forceFieldCreated = {} -- Track if force field is already created for each player
local lastActivityTime = {} -- Track the last activity time for each player
local function CreateForceField(player)
if not forceFieldCreated[player] then
if not player.Character:FindFirstChild("ForceField") then
local forceField = Instance.new("ForceField")
forceField.Visible = true
forceField.Parent = player.Character
for _,v in ipairs(player.Character:GetDescendants()) do
if v:IsA("MeshPart") then
v.Material = Enum.Material.ForceField
end
end
forceFieldCreated[player] = true
print(player.Name .. " force field created at: ", tick())
end
end
end
local function RemoveForceField(player)
if forceFieldCreated[player] then
local forceField = player.Character:FindFirstChild("ForceField")
if forceField then
forceField:Destroy()
for _,v in ipairs(player.Character:GetDescendants()) do
if v:IsA("MeshPart") then
v.Material = Enum.Material.Plastic
end
end
forceFieldCreated[player] = nil
print(player.Name .. " force field removed at: ", tick())
end
end
end
local function IsPlayerAFK(player)
return (tick() - lastActivityTime[player]) > AFKTimeThreshold
end
ActiveEvent.OnServerEvent:Connect(function(player)
lastActivityTime[player] = tick() -- Update last activity time for the player
if IsPlayerAFK(player) then
CreateForceField(player)
else
RemoveForceField(player)
end
end)
Focused.OnServerEvent:Connect(function(player)
lastActivityTime[player] = tick() -- Update last activity time for the player
end)
UnFocused.OnServerEvent:Connect(function(player)
if IsPlayerAFK(player) then
CreateForceField(player)
else
RemoveForceField(player)
end
end)
local function UpdateLastActivityTime(player)
lastActivityTime[player] = tick()
end
-- Detect movement
local function OnCharacterMove(character)
local player = game.Players:GetPlayerFromCharacter(character)
if player then
UpdateLastActivityTime(player)
print(player.Name .. " movement detected at: ", tick())
end
end
game.Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function(character)
character:WaitForChild("HumanoidRootPart").Changed:Connect(function()
OnCharacterMove(character)
end)
end)
end)
-- Check AFK state every second and apply/remove force field accordingly
while true do
wait(1)
for player, lastActivityTick in pairs(lastActivityTime) do
local remainingTime = math.max(0, AFKTimeThreshold - (tick() - lastActivityTick))
print(player.Name .. " timer at: " .. remainingTime .. " seconds")
if remainingTime == 0 then
if IsPlayerAFK(player) then
CreateForceField(player)
else
RemoveForceField(player)
end
end
end
end