- 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?
- 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.
- 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)()