_G. usage, improvements, and feedback

Hello, I am currently in the development stages of a tycoon game based around the SCP genre and I’d like to share some core functionality and get the community’s thoughts and opinions. My code base is entirely around _G. now after some digging, I’ve found it to be no different from requiring modules, however, many people have different opinions about the use of _G…

Here is an example of a _G. class created on the server, to act as a custom singleton to access all of my most used functions.

_G.Architect = {}

_G.Architect.Services = {
	["Workspace"] = game:GetService("Workspace");
	["workspace"] = game:GetService("Workspace");
	["Lighting"] = game:GetService("Lighting");
	["ServerStorage"] = game:GetService("ServerStorage");
	["ReplicatedStorage"] = game:GetService("ReplicatedStorage");
	["Players"] = game:GetService("Players");
	["Chat"] = game:GetService("Chat");
	["CollectionService"] = game:GetService("CollectionService");
	["RunService"] = game:GetService("RunService");
	["Run Service"] = game:GetService("RunService");
	["ServerScriptService"] = game:GetService("ServerScriptService");
	["HttpService"] = game:GetService("HttpService");
	["DataStoreService"] = game:GetService("DataStoreService");
	["DataStore"] = game:GetService("DataStoreService");
	["ScriptContext"] = game:GetService("ScriptContext");
	["TweenService"] = game:GetService("TweenService");
	["TS"] = game:GetService("TweenService");
	["SSS"] = game:GetService("ServerScriptService");
	["MPS"] = game:GetService("MarketplaceService");
	["MarketPlaceService"] = game:GetService("MarketplaceService");
	["Debris"] = game:GetService("Debris");
	["Teams"] = game:GetService("Teams");
	["PathfindingService"] = game:GetService("PathfindingService");
	["PFS"] = game:GetService("PathfindingService");
	["PhysicsService"] = game:GetService("PhysicsService");
}

_G.DataStoreVersion = 3

function _G.Architect:GetService(s)
    return ModuleLoad(s)
end
function _G.Architect:SendSnackBar(plr,txt)
	
	_G.Architect:SendClient(plr,"SnackBar",txt)
end
function _G.Architect:SendClient(plr,...)

	game.ReplicatedStorage.Remotes.Post:FireClient(plr,...)
end

It’s important to note that _G. is static, unchanging, and is declared at the start once.
I have no noticed any performance issues related to this use of _G. however, I’d like anyone’s opinions or criticisms about the use of _G., how it may be improved, and some issues I may encounter on the way?

Using _G. makes my code easier to expand, debug, and overall organize. I, however, do not want to be building myself a trap to fall into, with memory leaks, and etc.

Thanks

2 Likes

Best practice would be to move these into a module inside ServerScriptStorage or ServerScriptService. Instead of modifying _G, just modify the Architect table and return it. That way, you have no implicit dependency on Architect table. You also will know for sure that the table is already loaded in. It’s also better for debugging and organizing since you know the source of the Architect table. Organization is improved as well since you know where it is from for certain.

_G shouldn’t be really used for anything. There’s almost always a better alternative to _G.

6 Likes

A common truism of programming is that global variables are bad, but the way you’re using them is completely fine. The big problem with having global state is that you’ve no idea what may or may not modify it - but if you know that you’re not going to modify it anywhere, it’s completely fine. There’s not really an advantage to using ModuleScripts in your case, that’s just another way of having global variables.

The only issue that may come up is a kind of race condition - if some script depends on this global state being set, but it runs before the script that sets the state, that can lead to bugs. If you have some mechanism to guarantee that the above code runs before anything that depends on it then that won’t be a problem. You could for example put all of the above in a ModuleScript and have anything that depends on it require it. It can just return nil, and dependent scripts can discard the return value. The point is just that all of the global state gets set before it gets used.

4 Likes

The only issue that may come up is a kind of race condition - if some script depends on this global state being set, but it runs before the script that sets the state, that can lead to bugs.

The way I solved this was _G.Architect is instantiated in one server script at the start before any modules are required. The entire game operates around one script, the rest is modules.

I simply wanted a way to basically do:

game:GetService("MyCustomService")

which is where this idea stemmed from.