I frankly cannot get a straight answer from anyone I’ve asked, I assume it’s as easy as checking when someone left from within a serverscript.
Why would you want to detect it from the client?
From my previous experience, a player leaving the game has barely any time for remote events.
If you would check the leaving from the client and send an event to the server, the server will most likely return an error, because the player argument sent with the client → server remote event communication would become nil shortly after.
Still, you could theoretically do:
local plr = game:GetService("Players").LocalPlayer
game:GetService("Players").PlayerRemoving:Connect(function(leavingPlayer)
if leavingPlayer == plr then
--the client executing this is leaving, execute whatever code with that knowledge
end
end)
Hope I helped!
That’s what I was thinking and the problem I was expecting to hear. I’m just really trying to avoid doing things on the server as much as possible because I’m trying to be super cautious of lag.
Thank you for your help!
Btw how exactly would I test a script where someone is leaving the server alone?
Can you explain?
Like one person in the server and they leave?
In that case the code would still work, but whatever remote event was fired might not execute of it’s lengthy, because the server instance will be deleted.
Generally speaking, the server can handle quite a large amount of tasks, and any “lag” that happens is client sided most of the time.
I’d also say that servers have a “harder” time (not noticeable at all) handling remote events than just receiving the event itself (detecting remote event fired from client vs detecting client disconnect)
In any way, you should probably not rely on clients to detect things, first because clients can lag and cause delays, and also because any potential exploiters will have an easier time manipulating the game.
Sorry for the rant lol
Let’s say I want to change something on the serverscript that is detecting the player leaving, with no remote events. Now let’s say I have to stop playing to actually test this, how would I see if it’s working, and continue from there. Because I would be stopping the playtest.
Let me just explain the script I’m making, it will be easier.
I have a custom inventory script, which holds object inside the player, basically like a custom hotbar. When the player leaves, I’m trying to put the objects outside of their inventory and back into the workspace at the position they left.
I however cannot leave to test this, as if I leave then I cannot see if it put the part where it is being told to go.
Start a local server instead of using Play mode. This will start 2 new studio windows: 1 for the server instance, and then 1 for the player joining. From the player window, make your changes to the world (e.g. pick up items), and then close the player’s studio. Then look at the server studio instance and check if the workspace was returned to normal state as expected (e.g. those items are where you want them to be after the player leaves).
I didn’t even know that was possible, how do I do that?
Under the Test tab, in the Clients and Servers category of the ribbon bar, select the dropdown that says Local Server, select the other dropdown that says 1 player, and then click the Start button. This feature is useful for testing multiplayer scenarios or player joining and leaving tasks.
Woah, this surely will be super helpful. Thank you!
I however have one more question, why exactly is it not allowing me to even see my character after I leave? I’m trying to get it to place the object back at the CFrame of the player, and this is what I have.
Players.PlayerRemoving:Connect(function(player)
player.CharacterRemoving:Connect(function(char)
character = char
end)
print (character)
print (character.HumanoidRootPart.CFrame)
hcfx = character.HumanoidRootPart.CFrame.Position.X
hcfy = character.HumanoidRootPart.CFrame.Position.Y + 2
hcfz = character.HumanoidRootPart.CFrame.Position.Z
print (hcfx, hcfy, hcfz)
(It’s just saying character is nil)
Unless character is a global defined outside this function, it is never defined by the time print
runs. The assignment inside the CharacterRemoving event handler will not run before the code after it, it will only set up the event listener before the rest of the code. Then, when the event actually fires, sure it sets character to char, but then it goes out of scope. And even if it is a global defined somewhere outside this code snippet, that assignment will always run after the usage.
It’d be better to reference the character from the player object.
Players.PlayerRemoving:Connect(function(player)
local character = player.Character
-- do stuff with character here
end)
It is defined outside yes, that’s why I’m wondering what is wrong.
Also when I tried that, it had the same outcome. I’ve been troubleshooting this for like 2 hours, I’ve tried A TON of different things. Always ends up as character coming out as nil.
I guess without seeing how it’s used outside this function, then I can’t fully answer your question. But, good practice is to keep variables scoped as small as possible to avoid tricky debugging situations where a variable could be modified from many places. It’s better to pass as a parameter where necessary, or define it just where you need it preferably. Global variables are a last resort.
I’ll just give you my rough script before I polish it. I like getting it to work before I make it good.
local Players = game:GetService("Players")
local character
local hcfx
local hcfy
local hcfz
local hotbar1
local hotbar2
local hotbar3
local hotbar4
Players.PlayerRemoving:Connect(function(player)
getPosition(player)
getHotbars(player)
local storedinv = Instance.new("Folder")
storedinv.Parent = game.ReplicatedStorage
hotbar1.Parent = storedinv
hotbar2.Parent = storedinv
hotbar3.Parent = storedinv
hotbar4.Parent = storedinv
dropItems(player)
print "Fired Dropitems"
end)
function getHotbars(player)
hotbar1 = player:FindFirstChild("Hotbar1")
hotbar2 = player:FindFirstChild("Hotbar2")
hotbar3 = player:FindFirstChild("Hotbar3")
hotbar4 = player:FindFirstChild("Hotbar4")
end
function getPosition(player)
character = player.Character
print (character)
print (character.HumanoidRootPart.CFrame)
hcfx = character.HumanoidRootPart.CFrame.Position.X
hcfy = character.HumanoidRootPart.CFrame.Position.Y + 2
hcfz = character.HumanoidRootPart.CFrame.Position.Z
print (hcfx, hcfy, hcfz)
end
function dropItems(player)
print "DropItems Fire Recieved"
task.spawn(function()
if #hotbar1:GetChildren() >= 1 then
print "Hotbar1 has children"
for i, current in ipairs(hotbar1:GetChildren()) do
print (current)
if current.ClassName == "Model" then
for i, v in ipairs(current:GetChildren()) do
if v:IsA("BasePart") then
v.CFrame = CFrame.new(hcfx, hcfy, hcfz)
end end
current.Parent = game.Workspace.GrabObjects
print (current.Parent)
else
current.CFrame = CFrame.new(hcfx, hcfy, hcfz)
current.Parent = workspace.GrabObjects
print (current.Parent)
print (current.CFrame)
end
end
end
end)
task.spawn(function()
if #hotbar2:GetChildren() >= 1 then
print "Hotbar2 has children"
for i, current in ipairs(hotbar2:GetChildren()) do
print (current)
if current.ClassName == "Model" then
for i, v in ipairs(current:GetChildren()) do
if v:IsA("BasePart") then
v.CFrame = CFrame.new(hcfx, hcfy, hcfz)
end
end
current.Parent = game.Workspace.GrabObjects
print (current.Parent)
else
current.CFrame = CFrame.new(hcfx, hcfy, hcfz)
current.Parent = workspace.GrabObjects
print (current.Parent)
print (current.CFrame)
end
end
end
end)
task.spawn(function()
if #hotbar3:GetChildren() >= 1 then
print "Hotbar3 has children"
for i, current in ipairs(hotbar3:GetChildren()) do
print (current)
if current.ClassName == "Model" then
for i, v in ipairs(current:GetChildren()) do
if v:IsA("BasePart") then
v.CFrame = CFrame.new(hcfx, hcfy, hcfz)
end end
current.Parent = game.Workspace.GrabObjects
print (current.Parent)
else
current.CFrame = CFrame.new(hcfx, hcfy, hcfz)
current.Parent = workspace.GrabObjects
print (current.Parent)
print (current.CFrame)
end
end
end
end)
task.spawn(function()
if #hotbar4:GetChildren() >= 1 then
print "Hotbar4 has children"
for i, current in ipairs(hotbar4:GetChildren()) do
print (current)
if current.ClassName == "Model" then
for i, v in ipairs(current:GetChildren()) do
if v:IsA("BasePart") then
v.CFrame = CFrame.new(hcfx, hcfy, hcfz)
end end
current.Parent = game.Workspace.GrabObjects
print (current.Parent)
else
current.CFrame = CFrame.new(hcfx, hcfy, hcfz)
current.Parent = workspace.GrabObjects
print ("parent = ",current.Parent.Name)
print (current.CFrame)
end
end
end
end)
end
This essentially is saying “If the player leaves, get all the custom hotbars and save them before they delete, then put the contents of them back into the workspace, at the CFrame the character was.”
There’s a different issue here: If I’m not mistaken, this is trying to parent the items to workspace from the client, which will never show up on the server because of filtering (client changes do not replicate to server). ReplicateStorage, despite its name, is no exception to this rule - You have to parent it from the server. Give me a few minutes and I’ll write an example.
Does your server know what’s in your player’s inventory? This is critical information to be able to do it from the server properly
This is on a serverscript in serverscriptservice.
Okay nevermind then! Just a minute.
The inventory was created and is changed by only the server, the change requests are coming from localscripts
Any thoughts yet on what might be the issue?
Just to clarify again, the problem is it’s not returning the character, instead the character is showing up as nil.