Party Cross-Server Matchmaking Help

I’m currently developing a game that requires a party cross-server matchmaking system (Having a group of players join a queue with other random players. Eg. A group of 2 enter a queue and join a game with 2 random players, or another group of 2).

I’ve looked up some approaches to this system, and concluded that MemoryStoreService is what I’m looking for. Problem is I am having trouble implementing the “Party” feature, and I am unsure whether I should be using the Queue or Sorted Map primitive data structures. I am also unsure on how reliable MemoryStoreService actually is, and of any possible issues (Eg. Limits, latency issues, request outages, etc.) that I should be aware of.

In short, I am looking to build a scalable, robust and custom match making system that allows players to globally queue up as a party, or by themselves.

I am not looking for any exemplar code (Though any code is appreciated), but instead an explanation or nudge to point me in the right direction!

2 Likes

Hopefully I can be helpful, but I am going to start out by saying I have never used MemoryStoreService, And My experience is coming from My DataStore knowledge, as well as this MemoryStoreService Community Tutorial which I highly recommend you check out.

Memory store Service is the right choice. Lets first establish why, just in case other people Mention other methods, and as well fill in any gaps in your knowledge and understanding. You had 3 choices that are common for a system like this, that you could have used. MemoryStoreSerivce, DatastoreService, and MessagingService . You could “makes these work” for a system like this, but Its highly not recommended and heres why:


MessagingService Is NOT meant to be used as a reliable method, It isn’t a guarantee.

As stated in the Roblox docs on MessagingService: “Delivery is best effort and not guaranteed. Make sure to architect your game so delivery failures are not critical.”


Datastores are slow, Yield, and meant for Session based data (Data that is saved and that no data is lost, and there is no expiration time). You need something that is still reliable but server wide. You don’t need to save data if there is no servers. So because of this, we can roll out datastores.


Ok, so we have decided the other 2 options are unfit, and memory stores are the way to go. Lets talk about it itself.

From the roblox docs on MemoryStoreService:

" MemoryStoreService is a high throughput and low latency data service that provides fast in-memory data storage accessible from all servers in a live session. Memory Stores are suitable for frequent and ephemeral data that change rapidly and don’t need to be durable, because they are faster to access and vanish when reaching the maximum lifetime"

MemoryStores are meant for changes rapidly, for fast memory and accessibility. This is the best server so scaling up and allow all server access and meant for this.

These limited are quite lose and you shouldn’t need to worry, unless your game becomes really big on the platform and hundreds of thousands are using this system at once.

Here are the main limits

Memory Size Quota

The memory quota limits the total amount of memory that an experience can consume. It’s not a fixed value. Instead, it changes over time depending on the number of users in the experience according to the following formula: 64KB + 1KB ⨉ [number of users].


Data Structure Size Limits

For a single sorted map or queue, the following size and item count limits apply:

  • Maximum number of items: 1,000,000

  • Maximum total size (including keys for sorted map): 100MB


Api Request Limit:

“For API request limits, there’s a Request Unit quota applies for all MemoryStoreService API calls, which is 1000 + 100 * [number of concurrent users] request units per minute. Additionally, the rate of requests to any single queue or sorted map is limited to 100,000 request units per minute” …

These limits are Huge! Although if your worried, roblox has a Best Practices docs on Memory Stores to help with this!


It looks like you can use Both Queues and Sorted Map. Personally, I would use Sorted Maps, as it will allow you to get more organized and for me, Its easier to read and understand. I recommend checking out MemoryStoreService Community Tutorial if your totally lost after this explanation.

I would Make the Key the PrivateServerId that you would make with TeleportService:ReserveServer(), and then the Value with the players that have already Joined. That way, you can know when a server a full. When you find a server that is full, you remove it from the sorted map.

Here is a rough Example, And we will Assume the Max Server Amount is 4:

local players = game:GetService("Players")
local PlaceID = nil --PlaceId your teleporting too
local TeleportService = game:GetService("TeleportService")
local memoryStoreService = game:GetService("MemoryStoreService")
local TeleportServersMap = memoryStoreService:GetSortedMap("TeleportServers")

local function FindAServer(PartySize, PlayerInstanceList)
	local success, data = pcall(function()
		return TeleportServersMap:GetRangeAsync(Enum.SortDirection.Descending, 200) -- return the data
	end)

    local FoundServer = false

	if success and typeof(data) == "table" then 
		for _, info in ipairs(data) do
			local key = info.key
			local value = info.value 

            if #value >= 4 then --If its full, we will remove it from the list reguardless
                TeleportServersMap:RemoveAsync(key) 
            end

             if FoundServer == false and #value + PartySize <= 4 then 
                FoundServer = true

                if #value + PartySize == 4 then
                   TeleportServersMap:RemoveAsync(key) 
               else 
                 --Update the Value here with the new party members
                end

             TeleportService:TeleportToPrivateServer(PlaceID, key, PlayerInstanceList) --Teleport the players here

             end
			
		end
	elseif typeof(data) == "string" then
		warn(string.format('failed to fetch TeleportServersMaps data for reason of: %s', data)) 
	end

    if FoundServer == false then 
        local Code = TeleportService:ReserveServer(PlaceID)
        local success, err = pcall(function()
       local UserIDs = {}

       for i, player in PlayerInstanceList do
           table.insert(UserIDs, player.UserId)
       end

	TeleportServersMap:SetAsync(Code, UserIDs, 30) -- add Server. If no one joins in 30 seconds, the server is removed from map. 
	end)

	if not success then warn(string.format("failed to set %.f's data for reason of: %s", plr.UserId, err)) end -- log error
       TeleportService:TeleportToPrivateServer(PlaceID, Code, PlayerInstanceList) --Teleport the players here if no servers where found
    end
end

Basically:

→ Its gets the current servers
→ if a server has room, the members join that server
→ If a server does not have enough room, it moves on in the list
→ if no server is fit for the amount of people, It will make a server
→ It will add this server to the list so others can join and fill it up.
→ Once it reaches the max, server is removed from list

This is a basic idea and more code is needed for this to be improved on. Let me know if you have any questions. If your confused about this code, I recommend you check the community tutorial and I’m happy to answer any questions you may have!

6 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.