In which the error caused the Game Logic script to break (Requiring me to restart the relevant server). Ive had multiple similar issues in the past and have just normally done a quick fix of a sanity check.
My confusion is however, surely there is another option than putting a sanity check (e.g if v then) after every single line that has the risk of blowing up the entire game code? Is there something that can be implemented that completely resets the game code if an uncommon glitch like this occurs (E.g. The code detects it has not progressed in a while implying it has errored, and to then reset the script fully as a result?) As the alternative is adding âif v thenâ for example to every other line to give the code no ability to throw back an error. How do other games that run on a similar game-logic round based system deal with an issue like this?
Personally, I use Typescript to code my games which includes a lot of Dependencies to avoid that kind of stuff. Although, how the code is compiled and translated into lua is very long because itâs not written by a human, nevertheless it still doesnât affect the game at all. So all I could tell you is that sanity checks, whether you have a few or a huge amount wonât affect the performance of the game.
pull the annoying checks into a function (but then, youâll have to check that functionâŚ)
embrace the errors, ignore checks, and use pcall at whatever level you want to see if the whole chain of calls succeeded without explicit checks at every level.
The third one is similar to exception handling in java or C++.
The upside is that if youâre conservative about how often you call pcall you can write code with a lot less error handling.
The downside is that it doesnât seem to be a very common way people write code in lua (although it is in other languages) and it may be slower than checks.
Basically, when youâre in a function and calling another function that might fail: if you are able to handle the error right there, use pcall and do a check. If you are not able to handle the error, just let it âbubble upâ to whoever is calling you.
I think itâs because Luau is sandboxed and doesnât crash the entire application when it errors, unlike normal C++, Java or any programming language thatâs not meant specifically for games. Also, just putting pcalls everywhere can be several times worse than just, fixing the error? I donât think itâs a good idea to encourage hiding problems in your games behind a pcall-wall until the bugs pop up, leading to painful debugging because important errors just arenât there.
This seems like you can make Character a variable and do a guard statement instead.
for _, v in pairs(Players) do
local Character = v.Character
if not Character then
continue
end
...
end
You could also pack together statements with and or or so thereâs only really one if, and/or use multi-line if-statements for your code to look cleaner
if not x or --Checks and actual code are cleanly separated by the 'then' statement
not y or
not z or
not w
then
TacticalNuke()
end
C++ and Java donât crash the program when they throw, they go up the stack until they hit a try/catch block (their pcall equivallent).
Agreed! I donât suggest putting pcalls everywhere. Only pcall if you can actually do something about the error right there. If you canât, let it bubble up to a higher call level:
local function SomethingThatCanError(playerName)
local player = game.Players[playerName] -- can be nil, ignoring that
player.Character.Head.BrickColor = BrickColor.Random() -- can error, ignoring
end
local function HigherLevelThatCanError()
SomethingThatCanError("Player1") -- this call can error but we would just quit the function if it does
SomethingThatCanError("Player2") -- same. This won't run if the above errored, just like if we had used a guard statement
end
-- high level code catches low level errors
while task.wait(1) do
local success, err = pcall(HigherLevelThatCanError)
if not success then
print("Error we caught:", err)
end
end
This is a good point, and the lack of support from the debugger for this sort of thing is part of the reason itâs not commonly used. I agree with you that you could be hiding bugs this way, but I also think you can hide bugs with guard statements too:
local character = player.Chracter
if not character then return end
Because you mispelled Character, your function will always silently fail. At least with pcall youâll be able to catch the exact error at some level, and it will give you more info than just âpass/failâ.
Solve the root (pun not intended) of the problem, rather than trying to fix the symptom.
Players in Roblox have an ability to reset their character at any time, they may die or simply have not loaded their character yet.
There is no way for the engine to guess what do you want to do in each of those cases. Should you ignore players without characters or wait for them to load? Should you punish players for resetting or not? Et ceteraâŚ
What I do is, I create a separate module that solely deals with keeping track of players HumanoidRootPart's
The module:
Roots = {}
game.Players.PlayerAdded:Connect(function(plr)
plr.CharacterAdded:Connect(function(char)
Roots[plr] = char.HumanoidRootPart
end)
--optional
plr.CharacterRemoving:Connect(function(plr)
Roots[plr] = nil --putting nil removes an entry altogheter - this solution ignores players without a character
end)
end)
return Roots
Main script:
local Roots = require(game.ServerScriptService.Modules.Roots) --example path to module
for _, root in pairs(Roots) do
root.CFrame = mapSpawns[rand:NextInteger(1,#mapSpawns)].CFrame + Vector3.new(0,5,0)
end
Thanks for the replies everyone, they have been helpful in giving me some insight to the issue. Considering the way the rest of my game is set up, it really seems that the best way is to do the pedantic sanity checks for all relevant lines, however grouping it all into a function to reduce the amount that I have to do this. Thanks!