How to destroy a server when all the players have left it?

Hi.

I have made a VIP server system for my game, everything works perfectly. The only problem is, is that once a player creates a server, it is there forever and I don’t know how to destroy it, because I am teleporting the player to a different place.

The code:

-- Services
local DSS = game:GetService("DataStoreService")
local RS = game:GetService("ReplicatedStorage")
local TS = game:GetService("TeleportService")
local Players = game:GetService("Players")

--DataStores
local ServerAccessCodesDS = DSS:GetDataStore("ServerAccessCodesDS.")
local ServerNamesDS = DSS:GetDataStore("ServerNamesDS.")
local LocalOwnedServersDS = DSS:GetDataStore("LocalOwnedServersDS.")
local CustomServerJoinCodesDS = DSS:GetDataStore("CustomServerJoinCodesDS.")
local GlobalPrivateServerDS = DSS:GetOrderedDataStore("GlobalPrivateServerDS.")
local PrivateServerOwnerIdDS = DSS:GetDataStore("PrivateServerOwnerIdDS.")
local ServerInfoDS = DSS:GetDataStore("ServerInfoDS.")

local value = Instance.new("IntValue")
value.Parent = script.Parent
value.Name = "MaxServers"

--Paths
local menu = script.Parent.Parent
local createServerButton = menu.Frame.CreateServer.CreateButton

local yourServersButton = menu.Frame.YourServers_Button
local joinServerButton = menu.Frame.JoinByCode.JoinButton

local teleportGui = RS:WaitForChild("TeleportingGui")

-- Vars
local plr = menu.Parent.Parent


local psid = game.PrivateServerId
local servers = {}
local JoinServerCodeEntered;

pcall(function()
	for i,v in pairs(LocalOwnedServersDS:GetAsync(plr.UserId) or nil) do
		table.insert(servers, v)
		warn(plr.Name.." PS codes are: "..v)
	end
end)

function GenerateServerInviteCode()
	while true do
		local code = math.random(111111,999999)
		if not ServerNamesDS:GetAsync(code) then
			return code
		end
	end
end

function SortGlobalPrivateSeversAsync()
	local pages = GlobalPrivateServerDS:GetSortedAsync(false, 100)
	
	for i,v in pairs(menu.Frame.ServerList:GetChildren()) do
		if v:IsA("Frame") then
			v:Destroy()
		end
	end
	
	local data = pages:GetCurrentPage()
	
	for i,v in pairs(data) do
		warn(v.value, v.key)
		task.wait(.2)
		local ServerInfoDictionary = ServerInfoDS:GetAsync(v.key)
		local ServerFrameClone = script.ServersTemplate:Clone()
		ServerFrameClone.Parent = script.Parent.Parent.Frame.ServerList
		ServerFrameClone.PlayerCount.Text = v.value.."/20"
		ServerFrameClone.ServerName.Text = ServerInfoDictionary.ServerName
		ServerFrameClone.JoinServerButton.MouseButton1Click:Connect(function()
			local code = ServerAccessCodesDS:GetAsync(v.key)
			
			TS:SetTeleportGui(teleportGui)
			
			TS:TeleportToPrivateServer(9619375620, code, {plr})
		end)
	end
	
end

if string.len(psid) >= 30 then
RS:WaitForChild("PrivateServerOwnerValue").Value = PrivateServerOwnerIdDS:GetAsync(psid)
else
	
end

script.Parent.RemoteEvent.OnServerEvent:Connect(function(plr, txt)
	JoinServerCodeEntered = txt
end)

joinServerButton.MouseButton1Click:Connect(function()
	if JoinServerCodeEntered ~= nil then
		local id = ServerNamesDS:GetAsync(JoinServerCodeEntered)
		local code = ServerAccessCodesDS:GetAsync(id)
		
		TS:SetTeleportGui(teleportGui)
		TS:TeleportToPrivateServer(9619375620, code, {plr})
	end
end)

function UpdateYourServersList()
	for i,v in pairs(menu.Frame.YourServers:GetChildren()) do
		if v:IsA("Frame") then
			v:Destroy()
		end
	end
	
	for i,v in pairs(servers) do
		local id = ServerNamesDS:GetAsync(v)
		local ServerInfoDictionary = ServerInfoDS:GetAsync(id)
		local YourServerFrameClone = script.YourServersTemplate:Clone()
		YourServerFrameClone.Name = v..i
		YourServerFrameClone.Parent = menu.Frame.YourServers
		YourServerFrameClone.JoinCode.Text = v
		YourServerFrameClone.ServerName.Text = ServerInfoDictionary.ServerName
		YourServerFrameClone.JoinServerButton.MouseButton1Click:Connect(function()
			local code = ServerAccessCodesDS:GetAsync(id)
			local teleportGui = RS:WaitForChild("TeleportingGui"):Clone()
			teleportGui.Parent = plr.PlayerGui
			TS:SetTeleportGui(teleportGui)
	        TS:TeleportToPrivateServer(9619375620, code, {plr})
		end)
	end
	warn("Successfully loaded ", plr.Name, "'s servers!")
end

createServerButton.MouseButton1Click:Connect(function()
    if value.Value == 1 then return warn("Max!") end
	value.Value = 1
	print(value.Value)
	local code, id = TS:ReserveServer(9619375620)
	local NewJoinCode = GenerateServerInviteCode()
	GlobalPrivateServerDS:SetAsync(id, 0)
	CustomServerJoinCodesDS:SetAsync(id, NewJoinCode)
	ServerNamesDS:SetAsync(NewJoinCode, id)
	ServerAccessCodesDS:SetAsync(id, code)
	PrivateServerOwnerIdDS:SetAsync(id, plr.UserId)
	
	local ServerInfoDictionary = {
		ServerName = script.Parent.GetServerName:InvokeClient(plr),
	}
	
	ServerInfoDS:SetAsync(id, ServerInfoDictionary)
	table.insert(servers, NewJoinCode)
	local success, errorMessage = pcall(function()
		LocalOwnedServersDS:SetAsync(plr.UserId, servers)
	end)
	
	UpdateYourServersList()
	SortGlobalPrivateSeversAsync()
end)

Players.PlayerAdded:Connect(function()
	if string.len(psid) >= 30 then
		GlobalPrivateServerDS:SetAsync(psid, #Players:GetPlayers())
	end
end)

Players.PlayerRemoving:Connect(function()
	if string.len(psid) >= 30 then
		GlobalPrivateServerDS:SetAsync(psid, #Players:GetPlayers())
	end
end)

game:BindToClose(function()
	if string.len(psid) >= 30 then
		GlobalPrivateServerDS:SetAsync(psid, 0)
	end
end)

UpdateYourServersList()

plr.CharacterAdded:Connect(function()
	UpdateYourServersList()
end)

while true do
	SortGlobalPrivateSeversAsync()
	task.wait(60)
end

Any help would be appreciated, thanks! :grin:

3 Likes

That’s strange because in normal scenario it closes when all the players have left.
Is there any problem with displaying servers?

You should use MemoryStore instead. Data transfers have a much higher limit, run faster, and automatically clear. This is how I handle a server list in one of my games.

i’m to lazy to read the whole code but just make a table with the players inside each server then loop through it every 5 seconds and if there is no players close the server

Why loop when you can use a PlayerRemoving event to check how many players are left?

Yeah there is, when all the players leave the server doesn’t destroy, because I don’t know how I can check if all the players leave the server of a different place

By the way, the games are under the same place:

image

Oh so just check amount of players when game.Players.PlayerRemoved

if it helps then you can use messaging service to delete servers
Ps: it is just an example with plr chatting

local Players = game:GetService("Players")
local httpService = game:GetService("HttpService")
local messagingService = game:GetService("MessagingService")

function callbackFunction(serviceData)
	local decodedData = httpService:JSONDecode(serviceData.Data)
	print(decodedData.sender," : ",decodedData.message)
end

Players.PlayerAdded:connect(function(player)
	player.Chatted:connect(function(msg) 
		local messageData = {}
		
		messageData["sender"] = player.Name
		messageData["message"] = msg -- filter message first!

		local encoded = httpService:JSONEncode(messageData)
		messagingService:PublishAsync("Chat", encoded)
	end)
end)

messagingService:SubscribeAsync("Chat", callbackFunction)

that works as well (Ignore this)

Or like this?

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local PlayerStore = DataStoreService:GetDataStore("Players")

function getNumberOfPlayers()
return #Players:GetPlayers()
end

if getNumberOfPlayers() == 0 then

-- i don't know what to do from this point
end

Yeah. But also call that function when

Players.PlayerRemoving:Connect(function()
    if getNumberOfPlayers() == 0 then
       -- and here you can PublishAsync the server that have just deleted
    end
end)
--and when it publish you can subscribe async and get that server that have already deleted 
messagingService:SubscribeAsync("Servers", callbackFunction)
1 Like

Just use MemoryService like I linked earlier. Every 5 second intervals, send data with the JobId and player list, and have it set to degrade after 5 seconds. Then from the title screen, read from the list of MemoryStore entries and load UI based off of that. If the server closes, it will clear after 5 seconds. Or you can manually remove it after the final player leaves. Though the same concept applies to a DataStore in that case.

Yeah, but I am confused on how to use MemoryService

local MessagingService = game:GetService("MessagingService")

local Players = game:GetService("Players")

function getNumberOfPlayers()

return #Players:GetPlayers()

end

Players.PlayerRemoving:Connect(function(player)

if getNumberOfPlayers() == 0 then

MessagingService:PublishAsync("Server closed!", 'Player-'..player.UserId)
MessagingService:SubscribeAsync("Servers", callbackFunction)
end
end)

All of the documentation and examples are there, and there’s this too:

Perhaps you can find some video tutorials covering it.

Also please do not use MessagingService as it’s severally limiting.

I guess you should to send there you indentify of server like it’s name or smth
and then in the callbackFunction you should to delete if from the list of servers or just delete if from the array of servers if you have one.

and put subscribe async out of check
Also i don’t know how you indentify those servers that’s why i can’t say what should you send in PublishAsync.

local function callbackFunction(name_or_indentify_of_server_here)
    -- delete you server from that list of servers
end
Players.PlayerRemoving:Connect(function(player)
    if getNumberOfPlayers() == 0 then
        MessagingService:PublishAsync("Server closed!", "Name or indentify of server")
    end
end)
MessagingService:SubscribeAsync("Servers", callbackFunction)

Something like this (this script is in the place the player is teleporting to)

local MemoryStoreService = game:GetService("MemoryStoreService")
local ServerClosed = MemoryStoreService:GetQueue("ServerClosed")

local Players = game:GetService("Players")



function getNumberOfPlayers()
	return #Players:GetPlayers()
end


Players.PlayerRemoving:Connect(function(player)
	if getNumberOfPlayers() == 0 then
		
		local addSuccess, addError = pcall(function()
			ServerClosed:AddAsync("Server closed!")
		
	end)
		if not addSuccess then
			warn("Err!")
		end
	end
end)

But how would I make this destroy the server frame?

I meant that you would constantly send updates saying that the server does exist, while including the server information in said update. Once the server closes, the memorystore will automatically clear and memorystores are temporary. You set how long you want them to last.

Here, this is the exact code I used for the actual game server:

local MS = game:GetService('MemoryStoreService')
local P = game:GetService('Players')
local serverList = MS:GetSortedMap('serverList')
local closing
game:BindToClose(function()
	serverList:RemoveAsync(tostring(game.JobId))
	closing = true
end)
repeat
	local server = {}
	for _,v in pairs(game:GetService('Players'):GetChildren()) do
		if not v:GetAttribute('Hide') then
			table.insert(server,v.UserId)
		end
	end
	if not closing then
		pcall(function()
			serverList:SetAsync(tostring(game.JobId),server,15)
		end)
	end
	task.wait(5)
until false

This is the code I run on the title screen:

local MS = game:GetService('MemoryStoreService')
local serverList = MS:GetSortedMap('serverList')
local R = game:GetService('ReplicatedStorage')
local event = R:WaitForChild('Request Teleport')
local TP = game:GetService('TeleportService')
local list = R:WaitForChild('List')
event.OnServerInvoke = function(p,a,b)
	warn('Teleport request obtained:\n'..tostring(p)..'/'..a..'/'..tostring(b))
	if not workspace:GetAttribute('ServerLock') and a == 'Auto' then
		TP:Teleport(4968816126,p)
		local result
		result = TP.TeleportInitFailed:Connect(function(player)
			if player == p then
				result:Disconnect()
				return
			end
		end)
	elseif a == 'JobId' and b then
		TP:TeleportToPlaceInstance(4968816126,b,p)
		local result
		result = TP.TeleportInitFailed:Connect(function(player)
			if player == p then
				result:Disconnect()
				return
			end
		end)
	end
end
repeat
	list:ClearAllChildren()
	for _,v in pairs(serverList:GetRangeAsync(Enum.SortDirection.Ascending,200)) do
		local server = Instance.new('Folder')
		server.Name = v['key']
		for _,q in pairs(v['value']) do
			Instance.new('Folder',server).Name = q
		end
		server.Parent = list
	end
	task.wait(5)
until false

Ignore the extra code, I was also handling other things in there as well. Plus this code is a bit older, though it still functions as intended.

So for the first piece of code, I add it into the game where the player is teleported, and the second script I use for the main script I posted?

Pretty much, but obviously you’ll have to figure out how to integrate or reference it yourself. Good luck :+1:

i dont think you can destroy a server because the server is not accessible due to us for certain reasons, if it somehow was roblox wouldnt have working games anymore