Optimizing MessagingService Server Browser

  1. What do you want to achieve?

I’m looking for areas to improve in my implementation of a server browser. This was a little tedious for me to put together since it’s the first time I’ve experimented with MessagingService. For me, formatting the tables of info so they could be sent over MessagingService and a RemoteEvent was the hard part, and it feels like a bunch of spaghetti code to me. Did I implement this correctly?

  1. What is the issue? Include screenshots / videos if possible!

I’m mostly concerned with scalability and efficiency of my current server browser, and I’m also unsure how I would even benchmark it when it’s tedious enough to test it with two accounts on separate devices. I also have questions about the feasibility of making it update as close to real time as possible, but I do understand there are limits to MessagingService.

  1. What solutions have you tried so far? Did you look for solutions on the Developer Hub?

I don’t know if I didn’t look hard enough, but I couldn’t find that many topics relating to these specific questions, and i’m also just looking for general improvements that can be made to my code.

In a server script in the hub server:

--[[CONSTANTS]]--
local ReplicatedStorage		 = game:GetService'ReplicatedStorage'
local ServerStorage			 = game:GetService'ServerStorage'
local ServerScripts			 = game:GetService'ServerScriptService'
local RunService			 = game:GetService'RunService'
local HTTP					 = game:GetService'HttpService'
local MessageService		 = game:GetService'MessagingService'
local Network				 = ReplicatedStorage.Network
local MessageThrottle		 = require(script.MessagingThrottle)
local FloodCheck			 = require(script.RemoteFloodCheck)
local ServerUpTime			 = 0
local StartTime				 = os.time()
local numServers			 = 0
local LatestInfo 			 = false --update debounce ig, only send info to clients if it actually updated
local ServersTable			 = {} --table to store current stats about server
--[[END CONSTANTS]]--
--[[VARIABLES]]--
local ServersUpdateRate		 = 30	--send clients latest servers info every num seconds (should be lower than the one used ingame)
local RemoteThrottleRate	 = 15	--how many times a remoteevent/function can be fired/invoked in a second, any exceeding requests are ignored
--[[END VARIABLES]]--
local function UpdateNumServers()
	numServers = 0 --manually count through the table, since #table doesnt work on dictionaries?
	for i,v in pairs(ServersTable) do
		if v ~= nil then
			numServers = numServers + 1
		end
	end
end

MessageService:SubscribeAsync('GameServersData', function(newData) --set a callback function to fire whenever a server sends data to this 'topic'
	newData = newData.Data --messaging service returns the supplied message in a table ('Data') included with unix time sent ('Sent')
	--print'hub recieved data from ingame server'
	UpdateNumServers()
	local exists = false
		--print'searching for existing server'
		for i,v in pairs(ServersTable) do
			if v['ServerID'] == newData['ServerID'] then
				if newData['Status'] == 'Closed' then --remove closing servers
					print'closed and removed server'
					ServersTable[tostring(i)] = nil
				elseif newData['Status'] == 'Open' then --update existing server stats
					print'server updated'
					ServersTable[tostring(i)] = newData
					exists = true
				end
				break
			end
		end
	if exists == false and newData['Status'] ~= 'Closed' then --add data for new servers
		ServersTable[tostring(numServers+1)] = newData
		print'added server'
	end
	UpdateNumServers()
	LatestInfo = false
end)

MessageThrottle.Throttle('InGameServers','UpdateRequest') --ping existing servers to send info to this newly started hub server


coroutine.wrap(function()	--send server info to clients on an interval seperate from main thread
	while true do   --theres probably a better way to time/throttle updates so it scales up well
		wait(1/2)
		ServerUpTime = (os.time() - StartTime)
		if os.time()%ServersUpdateRate == 0 then 
			if LatestInfo == false then
				Network.UpdateServers:FireAllClients('Internet',ServersTable)
				LatestInfo = true
				print'updated server data sent to clients'
			end
		end
	end
end)()

In a local script in the hub server:

--[[CONSTANTS]]--
local ReplicatedStorage	 = game:GetService'ReplicatedStorage'
local Network			 = ReplicatedStorage.Network
local LocalServersData	 = {}
--[[END CONSTANTS]]--
Network.UpdateServers.OnClientEvent:Connect(function(ServersType,ServersTable)
	local UpdatedServers = ServersTable
	--print(UpdatedServers['1']['Region'])
	--print(UpdatedServers['1']['ServerName'])
	--print(UpdatedServers['1']['PlayerList']['1'])
	if ServersType == 'Internet' then
		LocalServersData = UpdatedServers
		UpdateInternetDisplay()
	elseif ServersType == 'Friends' then
		
	elseif ServersType == 'Favorites' then
		
	elseif ServersType == 'History' then
		
	end
	print'client recieved updated server data'
end)
UpdateInternetDisplay() --updates gui, not included in this post
print'client done'

In a server script in the INGAME server

--[[CONSTANTS]]--
local HTTP						 = game:GetService'HttpService'
local RunService				 = game:GetService'RunService'
local Players 					 = game:GetService'Players'
local ReplicatedStorage			 = game:GetService'ReplicatedStorage'
local ServerStorage 			 = game:GetService'ServerStorage'
local MessageService			 = game:GetService'MessagingService'
local MessageThrottle			 = require(script.MessagingThrottle)
local hostServerInfo			 = HTTP:JSONDecode(HTTP:GetAsync('http://ip-api.com/json/')) --http get that returns table of stuff about host server
local ServerUpTime				 = 0
local UpdatedData				 = false
local CurrentServerData			 = {}	--table of server data we'll send back to hub servers to be displayed in the server browsers
CurrentServerData['Status']		 = 'Open' --keep track of server status
CurrentServerData['ServerID']	 = game.JobId	--unique server identifier
CurrentServerData['ServerName']	 = 'Official Dedicated Server'	--default name
CurrentServerData['CurrentMap']	 = 'None'	--current map in play
CurrentServerData['Region']		 = hostServerInfo.regionName	--host server's location
CurrentServerData['RunTime']	 = tostring(os.time())	--unix time of when server started, used to find how long a server has been running
CurrentServerData['MaxPlayers']	 = 12	--max amount of players
CurrentServerData['PlayerList']	 = {}	--table for storing list of current players
--[[END CONSTANTS]]--
--[[VARIABLES]]--
local ServersUpdateRate			 = 60	--send hub servers latest game info every num seconds
--[[END VARIABLES]]--

local function UpdateServerPlayerList()
	CurrentServerData['PlayerList'] = {}
	for i,v in ipairs(Players:GetPlayers()) do --only store the player list of userids
		CurrentServerData['PlayerList'][tostring(i)] = tostring(v.UserId) --change the index to a string to avoid mixing the indices?
	end
	UpdatedData = false
end

local function UpdateCurrentServerData()
	if UpdatedData == false then
		UpdateServerPlayerList()
		UpdatedData = true
		MessageThrottle.Throttle('GameServersData',CurrentServerData) --module that throttles our publish requests
		print'sent server data to hubs'
	end

end

Players.PlayerAdded:connect(function(newPlr)
	UpdateServerPlayerList()
end)
Players.PlayerRemoving:connect(function(oldPlr)
	UpdateServerPlayerList()
end)

MessageService:SubscribeAsync('InGameServers', function(newData) --listen for requests from hub servers for updated server info
	newData = newData.Data
	if newData == 'UpdateRequest' then
		UpdatedData = false
		UpdateCurrentServerData()
	end
end)

game:BindToClose(function()
	print'server closing'
	UpdatedData = false
	CurrentServerData['Status'] = 'Closed'
	--if RunService:IsStudio() then return end
	UpdateCurrentServerData()
	wait(3) --yields game before close
end)

coroutine.wrap(function()
	while true do
		wait(1)
		ServerUpTime = os.time() - tonumber(CurrentServerData['RunTime'])
		if os.time()%ServersUpdateRate == 0 then
			print'attempting to update current server data'
			UpdateCurrentServerData()
		end
	end
end)()
1 Like