Hi there! I’ve been experiencing an issue for a while (since around 2023?) which seems to plague just about every instance of a genre of game I’ve been working on for the last few years.
In short, the games are “mining” focused - it relies on communication from the client (via the mouse) to “mine” blocks and generate new ones in its vicinity. Naturally, and especially in certain specific circumstances, this can cause severe stress on the server when a very large number of these events are occurring per second. However, it is necessary that this update is immediately visible to the client.
In more “tame” cases, this may cause the server to pause for a brief moment before it resumes and the player is able to continue as normal. Occasionally, though, ping-related issues can become extremely detrimental to the experience:
- In the first case, the client is mostly uninvolved. A block is mined and a cave is generated, which usually contains many more blocks than would normally be found by the player’s direct interactions. Sometimes, caves are so large that they seemingly overload the server and ping spikes massively to numbers as high as 30k (meaning the server will not respond for thirty seconds after generation). Through a lot of trial and error I’ve found a few routes of optimization for this, but it does still happen occasionally for very large caves, which can be frustrating at times as the player must wait this out (and do nothing, usually) until they can mine again.
- In the second case, the amount of blocks mined per second slowly increases as part of a “tactic” that some players of the game use. I’d like this tactic to be perfectly viable, but due to its continuously high mining rate, this seems to overload the server as well - ping suddenly spikes from its resting point to numbers as high as 5000ms, severely slowing down the client’s ability to interact with the server before dropping back down a handful of seconds after the player releases the mouse and stops mining. This seems to happen even if the received data is nothing too concerning (at the time of this screenshot, it was around 25kbps?)
As I said before, I’ve found a few solutions for the first problem even if they are imperfect - mainly just general optimizations. No matter how hard I optimize it though, this problem seems to stick around. What’s strange is that the extent of it seems to vary from game-to-game - as a specific example, the game where this code was taken from had to be transferred to another place (as the original owner had disabled their account) and the issues became far more severe. I’m not sure if there’s a priority system in place as to how much resource usage Roblox allocates to specific games and if that changed in some way when it was transferred.
In any case, I primarily have two questions - why does this happen in the first place, and what can I do to mitigate it? It hasn’t been the case forever - I noticed it in another game back in 2023 that hadn’t suffered from the same issues previously, and I noticed that it began to affect my own games shortly afterwards. I haven’t seen any posts that mention this issue, at least not directly, so any insight or vague ideas would be extremely helpful. Thanks!!
Here’s a simplified version of a server-sided function that fires whenever the client sends a request through a RemoteEvent to the server:
local function mineOre(plr,obj,abil)
if obj and not obj:GetAttribute('mining') and not (obj:GetAttribute('placed') and obj:GetAttribute('placed') ~= plr.Name) and not obj.Locked and not (abil and obj:FindFirstChild('Azure')) then
obj:SetAttribute('mining',true)
local newVectors = vectors
if convert(obj.Position.Y) < -4500 and convert(obj.Position.Y) > -5000 then -- rip.
newVectors = interVectors
end
for k,v in pairs(newVectors) do
local newPos = obj.Position + v
if convert(newPos.Y) <= 0 then
generateOre(getOre(newPos):Clone(),newPos)
end
end
_G.take[getConvertedPosition(obj.Position)] = nil
obj.Parent = nil
blooker -= 1
end
end
Here’s another simplified function, in the same script, which is called in the snippet above and handles inserting the newly generated blocks into Workspace:
function generateOre(ore,pos,override,cave)
if ore and ((not take[getConvertedPosition(pos)]) or override) then
take[getConvertedPosition(pos)] = true
_G.take[getConvertedPosition(pos)] = true
if getCaveNoise(pos) > 0.43 and getCaveNoise(pos) < 0.74 and convert(pos.Y) < 0 and not isPositionNoCave(convert(pos.Y)) then
local barrier = ore:Clone(); barrier.Parent = workspace
barrier.Color = Color3.fromRGB(144,144,144); barrier.Material = 'Slate'; barrier.Position = pos
local ct = getRandomCaveType()
if convert(pos.Y) <= -4500 and convert(pos.Y) > -5000 then if math.random(1,3) ~= 1 then ct = '' end end
local cave = caves[initializeCave(ct)]
cave.caveVectors[1] = pos
generateCave(cave)
cave.parent.Parent = workspace.MineFolders
barrier:Destroy()
else
ore.Parent = (cave and cave.parent) or workspace.Mine
ore.Position = pos; blooker += 1
end
end
end