Join private server by 5-digit code

Hello fellow developers, thank you for your time, and this is my issue:

I wish to achieve the following system:
a. A player is able to create a Private Server by clicking a button :heavy_check_mark:
b. The player will be teleported to the server after clicking the button. Then, the script would check if the server is a private one. If so, a TextLabel would become visible, displaying the current 5 digits of the Private Server Id. :heavy_check_mark:
c. Every single player is able to join any server by entering the code. Now, I would like to make it so that the player enters a 5-digit code and it corresponds to a valid 5-digit code of a server, they get teleported to that server. :x:

In case you may not have understood, I’ll provide examples.

  1. The player clicks the Create Server button.
    1

2.The player will be teleported to a freshly created Private Server. Upon joining, a TextLabel will appear bearing the first five digits of the game.PrivateServerId.
2

  1. Any player entering the correct code, in my case d19fe would be teleported to that private server. This is the part that I’m having issues with. Regardless, thanks for you time.
3 Likes

Sorry, but that is not how private servers work. They can only be accessed the first time players are teleported to them, and nobody else can teleport to them later. If you want to make this system im afraid you will have to compromise for a party system that teleports them all at once, and cannot be joined later. Good luck!

edit: I was wrong sorry. I looked into it a bit more and found something you might want to look into!
https://developer.roblox.com/en-us/api-reference/function/TeleportService/TeleportAsync

That is incorrect, private servers can be joined at any time as long as you have the private server ID that was generated when calling :ReserveServer. Pretty sure you can teleport to the game using the game’s JobID as well (not 100% sure about this one though) (update, you can’t).

UPDATE 3/15/22
This post was marked as a solution because I gave OP help in discord but it used unreliable methods so don’t use this. There now exists a way to do a safe implementation that doesn’t rely on the client, by using MemoryStoreService.

Further, I helped that user out on discord so that code really isn’t the actual solution.

What I’d do in this case is the following:
1- use TeleportService:ReserveServer which returns two strings:

  • the ReservedServerAccessCode
  • the JobId

So, by using the game’s PrivateServerId, we can use this as the key, and we can use the AccessCode as the value:
So on server 1, the “matchmaking” server:

local reservedServerAccessCode, privateServerId = teleportService:ReserveServer(placeId)

local memoryStoreQueue = memoryStoreService:GetQueue(privateServerId, 120) -- should return a queue object, second parameter, realistically we shouldn't need this available for more than 2 minutes and that's in some extreme circumstance where servers are abnormally slow. really, the teleport should error before it takes 120 seconds but yeah
memoryStoreQueue:AddAsync(reservedServerAccessCode, 120) -- 120 for the same reasons as above
teleportService:TeleportToPrivateServer(placeId, reservedServerAccessCode, {player})

Now, on the second server:

local privateServerId = game.PrivateServerId
local memoryStoreQueueForThisServer = memoryStoreService:GetQueue(privateServerId, 120)
local thisPrivateServerAccessCode = memoryStoreQueueForThisServer:ReadAsync(1) -- now, we have given the private server its own access code
thisPrivateServerAccessCode = thisPrivateServerAccessCode[1]

Now, I’d suggest using the private server ID’s first 6 or so characters as the access code for readability and management sake.

local thisServerAccessCode = thisPrivateServerAccessCode:sub(1,6) -- this will be the access code that needs to be shown to the player

-- now, we need to use MessagingService to create a connection that will use a GUID to return the access code to the main server:
messagingService:SubscribeAsync(thisServerAccessCode, function(message) -- this will be a table with 2 items: RequestedServerCode and a GUID which will be used to "return" the value to the main server
    message = message.Data
    if message.RequestedServerCode == thisServerAccessCode then -- technically don't have to check this but just in case
        messagingService:PublishAsync(message.GUID, thisPrivateServerAccessCode)
    end
end)

Now, back on the “main” server:

local guid = httpService:GenerateGUID(false) -- false because we don't want to wrap in brackets
local tempConnection 
tempConnection = messagingService:SubscribeAsync(guid, function(returnCode)
    teleportService:TeleportToPrivateServer(placeId, reservedServerAccessCode, {player})
    tempConenction:Disconnect()
end)
messagingService:PublishAsync(codeInputtedByPlayer, {RequestedServerCode = codeInputtedByPlayer; GUID = guid})

That should give you an idea to approach this.

Old reply

TeleportService | Roblox Creator Documentation

You could probably formulate some type of return system using MessagingService by doing something like this:

Server 1/“main” server:

local messagingService = game:GetService('MessagingService')
local teleportService = game:GetService('TeleportService')
local requestedID = a1b2c -- change this to whatever

messagingService:PublishAsync(requestedID, 'GetJobId')
local connection
coroutine.wrap(function()
    wait(5) -- timeout
    if connection.Connected then
        connection:Disconnect()
        -- return to client that the server with that ID doesn't exist
    end
end)()
connection = messagingService:SubscribeAsync(requestedID, function(message) -- this is going to act as our "returned" value which would be the access code
    local accessCode = message.Data
    connection:Disconnect() -- we want to disconnect so no further messages are received
    teleportService:TeleportToPlaceInstance(123456, accessCode, game:GetService('Players').user)
end)

Server 2:

local messagingService = game:GetService('MessagingService')
local teleportService = game:GetService('TeleportService')
local partyID = a1b2c -- the 5-digit code

local connection = messagingService:SubscribeAsync(requestedID, function(jobId) -- this is going to act as our "returned" value which would be the job id
    messagingService:PublishAsync(partyID, game.JobId)
end)
6 Likes

Your solution is much appreciated, however I just wanted to point out that it outputs the following error: Unable to cast value to std::string; This is the line: TS:TeleportToPlaceInstance(game.PlaceId, jobId, player)

My bad, you would have to use the .Data property of the message. Something like this should work:

connection = messagingService:SubscribeAsync(requestedID, function(jobId)
    connection:Disconnect()
    teleportService:TeleportToPlaceInstance(123456, jobId.Data --[[make sure to get the .Data property of the table on callback]], game:GetService('Players').user)
end)

You’ve quite lost me on the track, do you mind elaborating on the callback?

My bad haha.

So basically, when using :SubscribeAsync, the first argument of the function is a table which would look like this:

local message = {
    Sent = 1234523481 -- some number which is the unix timestamp (os.time())
    Data = 'something' -- this .Data property of the message can be any datatype as long as it's under 1000 characters
}

So, we have to use the Data property/entry of the message table to get the job ID.

local connection = messagingService:SubscribeAsync(requestedID, function(jobId) -- remember requestedID is the party code which is also the topic
    messagingService:PublishAsync(partyID, game.JobId) -- the JobId is what we are using to teleport the player to the place instance
end)
connection = messagingService:SubscribeAsync(requestedID, function(message) -- the message variable is the table I showed above
    connection:Disconnect()
    local jobId = message.Data -- the .Data property of the table is the JobId which will be used to teleport the player to the place instance
    teleportService:TeleportToPlaceInstance(123456, jobId, game:GetService('Players').user)
end)

you can use my code

script (ServerScriptService):

local ServerCode = DS:GetAsync(“Custom_PrivateServer_v2”)
local code = DS:GetAsync(“Custom_PrivateServer_v1”)
local Remotes = — your RemoteFunction Locate

– generate a random string code.
local function GenerateRandomString(length)
local newStr = “”
for _ = 1, length do
newStr …= string.char(math.random(65, 90)) – A-Z;
end

return newStr

end

Remotes.RemoteFunctions.OnServerInvoke = function(Player, Infos, Data1)

if Infos == "GetMyCode" then
	
	if game.MarketplaceService:UserOwnsGamePassAsync(Player.UserId, GamepassId) then --- you can put gamepass for if the player own the gamepass he can generate the code for first time if the datastore have no private server code else it will return false
		
		if not ServerCode  then
			
			ServerCode = GenerateRandomString(6) --- how much length for the code to access the private server
			
			if not code then
				code = TS:ReserveServer(Your Game Id) -- Your Game Id
				DS:SetAsync("Custom_PrivateServer_v1", code)
			end
			
			DS:SetAsync("Custom_PrivateServer_v2", ServerCode)
			
			return ServerCode
			
		elseif ServerCode and code then
			
			return ServerCode
			
		end
	else
		return false
	end
	
elseif Infos == "Join_PS" then
	
	if Data1 == ServerCode then
		for k,v in pairs(Players:GetPlayers()) do
			TS:TeleportToPrivateServer(7238326032, code, {Player})
		end
	else
		return false
	end
	
end

end

LocalScript (StarterGui):

local ReplicatedStorage = game:GetService(“ReplicatedStorage”)
local Remotes =— Your RemoteFunction Locate

local myCode = Remotes.RemoteFunctions:InvokeServer(“GetMyCode”)
local CodeText = script.Parent.Frame.PsBG.Code

if myCode == false then
	CodeText.Text = " -"
else
	CodeText.Text = " "..myCode
end

script.Parent.Frame.PsBG.Join.MouseButton1Click:Connect(function()
	local GetCheck_Join = Remotes.RemoteFunctions:InvokeServer("Join_PS", script.Parent.Frame.PsBG.TextBox.Text)
	
	if GetCheck_Join == false then
		script.Parent.Frame.PsBG.TextBox.Text = "Code Access not Valid"
		wait(2)
		script.Parent.Frame.PsBG.TextBox.Text = "Enter Server Id"
	end
	
	wait()
end)
2 Likes