Smooth Shutdowns - Update your game servers with ease

What this does:
When the game updates, the server will tell all clients that the server will shut down in 15 minutes. When 15 minutes have passed, all players will be moved to new servers.


Try it out here: https://www.roblox.com/games/6428522458/Devforum-better-shutdown
note: do not copy from this place. even though it is unlocked, it does not have the real code that this system uses

Why should you use this:
This system does 2 things that migrate to the latest update doesn’t. It doesn’t kick the player from the game and it doesn’t do it immediately without notice. With this system, you don’t have to worry about players losing progress because they were suddenly teleported to a new server.

I used the softShutdown script by merely for the transition between servers.


--Server Script
local players = game:GetService("Players")
local MarketPlaceService = game:GetService("MarketplaceService")
local TeleportService = game:GetService("TeleportService")
local gameID = game.PlaceId 
local versionCurrent = MarketPlaceService:GetProductInfo(gameID).Updated 
local checkDelay = 5
local warningTime = 15 * 60 -- 15 minutes
local startTime 


if (game.PrivateServerId ~= "" and game.PrivateServerOwnerId == 0) then
	-- this is a reserved server without a VIP server owner
	local waitTime = 5

	players.PlayerAdded:Connect(function(player)
		wait(waitTime)
		waitTime = waitTime / 2
		TeleportService:Teleport(gameID, player)
	end)

	for _,player in pairs(players:GetPlayers()) do
		TeleportService:Teleport(gameID, player)
		wait(waitTime)
		waitTime = waitTime / 2
	end
end

local remote = Instance.new("RemoteEvent")
remote.Name = "Shutdown"
remote.Parent = game:GetService("ReplicatedStorage")


while true do
	wait(checkDelay)
	if MarketPlaceService:GetProductInfo(gameID).Updated ~= versionCurrent then
                startTime = os.time() 
		break
	end
end

players.PlayerAdded:Connect(function(plr)
	remote:FireClient(plr, warningTime-(os.time()-startTime))
end)

for _, plr in pairs(players:GetPlayers()) do
	remote:FireClient(plr, warningTime)
end


wait(warningTime)


if (#players:GetPlayers() == 0) then
	return
end

if (game:GetService("RunService"):IsStudio()) then
	return
end

local reservedServerCode = TeleportService:ReserveServer(gameID)

for _,player in pairs(players:GetPlayers()) do
	TeleportService:TeleportToPrivateServer(gameID, reservedServerCode, { player })
end
players.PlayerAdded:Connect(function(player)
	TeleportService:TeleportToPrivateServer(gameID, reservedServerCode, { player })
end)
while (#players:GetPlayers() > 0) do
	wait(1)
end	
--local script
local player = game.Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")

game:GetService("ReplicatedStorage"):WaitForChild("Shutdown").OnClientEvent:Connect(function(warningTime)
	local versionGui = Instance.new("ScreenGui")
	local textLabel = Instance.new("TextLabel")
	textLabel.Position = UDim2.new(1, -10, 1, 0)
	textLabel.AnchorPoint = Vector2.new(1, 1)
	textLabel.Size = UDim2.new(0, 150, 0, 40)
	textLabel.BackgroundTransparency = 1
	textLabel.TextColor3 = Color3.new(1, 1, 1)
	textLabel.TextStrokeTransparency = 0
	textLabel.TextXAlignment = Enum.TextXAlignment.Right
	textLabel.TextScaled = true 
	textLabel.Parent = versionGui
	versionGui.Parent = playerGui

        local want = os.time() + warningTime
        while true do
            textLabel.Text = os.date('!%M:%S', want - os.time() )
            wait(1)
        end


end)

Alternative: this will be the best way but will require a little extra work. By saving a modulescript with an id you can kinda get the new place version by editing the value when you update the game. You will need to require the id of the module script in a loop and check the server’s version and the module scripts version. The reason I didn’t do this for this resource is because of that extra step. If you think that you can deal with it; feel free to use this method. Similarly, asset service uses the same process.

Alternative2: by using messaging service, it should be possible to compare server versions by using
game.PlaceVersion. This property gives you the current server’s version when not in studio. When a new server is created, you can simply alert all servers of the value and all servers will check if the version is equal to their current version.

Alternative 3: by using httpservice, you can tell all servers to shutdown on your command. This will require extra work but is a good method if you want more control.

Downsides to the used method: slow. GetProductInfo yields and takes a while to get info making visible delays in server shutdowns. If you can deal with that delay then this method is perfectly fine. This method also uses a loop to constantly check for new versions. Additionally, using this method makes it hard to get the actual updated content. Meaning you can only have a countdown and not additional info on why the server is shutting down. This method also prevents you from setting your own custom warning time when you update the game. It will use the time that you put on the last update.

This system is mostly the backend of the system. It’s your job to make it pretty and fit your game’s style.

Do you think this is well made for my first community resource?
  • yes
  • no

0 voters

would you use it
  • yes
  • no

0 voters

Do you have your own custom method to detect version changes? Comment Below!

20 Likes

You can replace this with

while warningTime > 0 do
      textLabel.Text = os.date('!%M:%S', warningTime)
	  warningTime -= 1
      wait(1)
end

Yes that will definitely work too.

To anyone who used this script, there were many deprecated things I missed from merely’s script. I fixed them and you should use the one that is posted now.

1 Like

Ive added a testing game where you can see what I mean by shutdowns at Devforum better shutdown - Roblox

Also, please note that this does not work well if your game relies heavily on reserved servers. However, I do believe that this can be overcome so I will look into alternative methods. If I find a solution, I will post again. Please enjoy!

previous update: In the client script, I changed the timer loop to use os.time math instead of decrementing the value. I did this because I noticed that the server and client clock are very far off as the warning time increases. This solution makes it sync in a hard to notice way to ensure that the server stops when the client’s number reaches 0.

Do I put the local script in StarterGUI?

You can if you want. I just prefer playerscripts

1 Like

Can you make this a model? I don’t want to copy and paste the script into my game as of now.

This model contains the gui I used in the game as well. Also note that I used a custom wait and heartbeat events for all the yielding. Oh and for this model, you can take the screengui and just put it in startergui.

3 Likes

Would it be possible to add a Sound:Play function to it, so when the countdown hits zero, a sound plays? I figure it would useful for players who are AFK but still in earshot of their computer so they know that the server had been shutdown for updates.

Yes its possible. Add this to the server script right after the wait. Make sure to use the .Ended:Wait() to ensure all players hear it before they leave

This does not work. I like it, I wish you would fix it.

1 Like

So I got your model and I put the gui in startergui, I tried testing it by being in the game, then making a change in studio followed by publishing, then I’d shut down all servers. However I’d just get kicked from the server, so how exactly should I be applying the softshutdown? Do I have to initiate it by physically triggering it (like in your touched part example)?

I think Roblox has done something that may broke it…

I tried this module before and I think It worked perfectly fine…

@darkpixlz

1 Like

Do you know of any SoftShutdown’s that are working?

well, I don’t know what happened to a lot of them but try this:

I know this one should work perfectly fine…