Server VS. Client VS. Module Scripts; What's the Difference?

As I was starting out scripting, I was extremely confused with all of the different types of scripts, but they actually have very important differences! This tutorial goes in-depth about all the different types of scripts & other info that may be very useful! Whether you are new, experienced, or looking to re-sharpen up your skills, this tutorial is for you! :bulb:

Client Scripts

Client Scripts

To understand client scripts, you must understand a few key things first!

  • Client Scripts run on the player’s device;
  • Client Scripts do not change items for other players;
  • & Finally, Client Scripts are prone to exploiters.

Table of Contents:

  1. Functionality
  2. Location
  3. Safety

I would highly recommend reading them in order.

ptsa: I will be referring to them as “LocalScripts” for the rest of this tutorial :dizzy:
Chapter 1: Functionality

Due to LocalScripts running on the Client, they do not change data for other players / the server, here is a comprehensive table explaining the functionality.


Action Changing Data Deleting Data Creating Data
Result Data changes for the client, other clients (or players) & server scripts cannot access the change. Data deletes only for the client once again-if you delete a part only the local player can see it deleted. Data creates only for the client, only LocalScripts running on the same client can access the data created.

Next up: Location.

Chapter 2: Location

LocalScripts cannot just be put anywhere - they must be in a location where they are able to run! Here is every single location you can put a LocalScript in Roblox Studio!

  • StarterPlayerScripts - Will automatically be placed in every player object under a folder.
  • StarterCharacterScripts - Will automatically be placed in every player character object.
  • StarterPlayer - Will automatically be placed in every player object - *not in a folder.
  • StarterGui - Will automatically be placed in every single player GUI.
  • StarterPack - Will automatically be placed in every player object under their Backpack.
  • Directly into the Backpack Object from a script
  • Directly in the Character Object from a Script
  • Directly in the Player Object from a Script
fyi - they can be placed anywhere, but these are the places that they will run :large_orange_diamond:

Next up: Safety.

Chapter 3: Safety

LocalScripts run locally - I think that’s been defined at this point. However, due to them running locally, this means that they are prone to exploiters as exploiters use, change, read, delete, and create data *locally. Here are my personal tips for safety when using local scripts:

  1. Always check / change critical data through a server script.

Exploiters can exploit many vulnerabilities if data is checked locally! If you check someone’s money from a local script, an exploiter could have already set that to an extremely high number, meaning that they could abuse your code.

Solution: Use a RemoteEvent to send a signal to a server script to check and send back the true data.


  1. Limit / Remove any Client Managed Values.

Try to limit / remove data that is created and/or managed solely by the client; it’s not safe.

Scenario: A Player has an in-game currency that is only managed by the Client and cannot be managed by the Server. It seems like a great approach if you don’t want other people to have access to your in-game currency data. However, now exploiters can easily change any values and/or data with ease, as you cannot implement an anti-cheat against it due to it being local.

Solution: Just make it accessible to all, and apply the previous knowledge.

I hope that you learned some about LocalScripts!


Server Scripts

Server Scripts

In order to understand ServerScripts, a few key details must be pointed out first.

  • Unlike LocalScripts, ServerScripts are managed by Roblox servers.
  • ServerScripts change data for everyone, not just the client.
  • & Finally, ServerScripts are not prone to exploiters!

Table of Contents:

  1. Functionality
  2. Location

I would highly recommend reading them in order

Chapter 1: Functionality

Server Scripts are managed by Roblox Servers, so data changed, deleted, or created happens for all clients. Data changed through LocalScripts will not be changed on the server. :sunny:

Server Scripts should be used to check / change values (such as money, xp, and basically all values) and to edit data you want to be changed throughout the entire server (CFrame, Color, and many other values) :compass:

Next Up: Location.

Chapter 2: Location

Similar to LocalScripts, ServerScripts will only run in certain locations, so here is a list of all of the locations I recommend they should be put.

  • ServerScriptService
  • Any Tools inside of the backpack / player
  • Inside the character if needed
  • Under certain object types in workspace
  • Workspace - Although I highly don’t recommend putting all your scripts in Workspace, it will run, but the code can be stolen by exploiters

ModuleScripts

ModuleScripts

ModuleScripts work differently than Client and Server scripts–they are used for storing data / tables and can be accessed by both Client and Server scripts.

Table of Contents:

  1. Functionality
  2. Access

I would highly recommend reading them in order

Chapter 1: Functionality

Module Scripts are not used as much to change data, they are used to store it inside of tables. They work very differently than other Script types.

Chapter 1a. - ModuleScript Basics

To start off your ModuleScript, you need to have a table with a return + your table name at the end.

MyTable = {
--PlaceHolder
}

return MyTable

To add data, you just add variables but do not type local.

Good Example:

MyTable = {
  PlaceHolder = "Hello! This is a PlaceHolder!"
}

return MyTable

Bad Example:

MyTable = {
  local PlaceHolder = "Hello! This is a PlaceHolder!"
}

return MyTable

Unlike Local/Server Scripts, ModuleScripts are mainly used to store data. Although they can run code, we won’t be going into that in these basics.

Next Up: Access.

Chapter 2: Access

To interpret stored data in ModuleScripts, we must use the require() attribute to send a request for the data.

Chapter 2a. - Accessing Data & Use Cases

So we use the require() attribute to request the data, but how do we do it? It’s actually very simple. Just put your ModuleScript object into the parenthesis and call it a day. Here is a code snippet:

local MyModule = game:GetService("ServerStorage"):WaitForChild("MyModuleScript")
local MyModuleData = require(MyModule)

Chapter 2b. - Finding variables inside the module.

Once you have the ModuleScript required, you can access any of the variables inside of it by using the good ol’ . Here is a code snippet:

ModuleScript:

MyTable = {
   PlaceHolder = "Hello! This is a PlaceHolder!"
}

return MyTable

ServerScript:

local MyModule = game:GetService("ServerStorage"):WaitForChild("MyModuleScript")
local MyModuleData = require(MyModule)

local PlaceHolder = MyModuleData.PlaceHolder

It’s that simple! Nothing else needed unless you are going very deep.


Thanks for reading! If you’ve got any issues, complaints, or feedback I’d love to hear your opinion down below, and if I’m missing and/or have any formatting issues, false information then please let me know!

By the way, this post took me several hours to make, so if you would like to support me, drop a follow, it’s easy and motivates me to make more posts like this!

if there is any tutorials you would like me to focus on, I would love to hear them, bye now :wave:
12 Likes
MyTable = {
  local PlaceHolder = "Hello! This is a PlaceHolder!"
}

insane

MyTable = {
  local PlaceHolder = "Hello! This is a PlaceHolder!"
}

Im cry

i copied the wrong code whoops :sob::sob:

fixed now thank you for the notice :clipboard:

I mean, using modulescripts purely to store data is good practice yeah, but why the “do not try to change, create or remove any data”? Using ModuleScripts to share information/communicate across scripts is very commonplace and actually recommended over the usage of e.g ValueObjects or BindableEvents or _G or shared.

This is only if you return a table in the ModuleScript, keep in mind ModuleScripts can return literally anything. Userdata proxies, instances, functions, strings, numbers, etc. And these values are also what will be returned by require()

Other than that tho the rest of the info is pretty sound from a quick read, nice post!

That was just the module script basics. I was explaining that inside of the table provided do not try to edit data. They can also return many values, you are correct! I was trying to explain just a few basics. Thanks for the tips, I’ll try to add that in my module script, and thanks for the complement!

1 Like
local MyTable = {}
MyTable.PlaceHolder = "Hello! This is a PlaceHolder!"

Patch notes V2, reworded module scripts to explain that they CAN run code inside of them :memo:

It’s not that apparent if the only explaination is just “although they can run code, we won’t be going into that in the basics”. It’s making module scripts appear as just a way to store data, even though both you and I know it’s not just that; but readers would just think so.

What I suggest is to at least make it as cohesive as possible. It should be placed near the text below:

that a bad idea btw
It will create a table and then will have to expand it :skull: for value to fit

if i were to learn everything from scratch ModuleScript should be treated as a function that runs only once and will store value it returned for rest of its existance
And by requiring it you will get value that it returned.
So first time requiring module also initiates it and every time you require it is just getting the result that it returned after initiation.
If that does make sense. :man_shrugging:

These two options are almost functional equivalents. Adding values to the table takes literally ~0ms. I am not ready to sacrifice hints for annotation via : for the sake of literally… 0.000001ms faster execution.

P.S

abc = {
Idk = function(self) end
}
abc:Idk() -- no hints but work
abc ={}
function abc:Idk()

abc:I -- yey, hints

Tomorrow, I will be removing the example code snippets from the module script examples as well as rephrasing it due to possible confusion I guess.

Hints for annotation?
Put a strict mode and write type for it (you have to do that anyway to keep up with LuaU optimization techniques).
I don’t really see why you would, define functions in this outdated way in the second option anyway.
Isn’t

abc.Idk = function(self)

end

Would be more readable?
More over, why would you use a colon here? You are not calling any value to your argument self.

“I’m not going to” is just an excuse for not writing a good code.
Also, why would you construct that in such a stupid way, wasting so much memory if you could just make a pointer to function?
Because you are trying to create a class shenanigan, as I see in your example.

function Idk(self)
end
local abc = {Idk=Idk}

That is just such an absurd example you provided that the annotation refuses to show :skull:

Funny thing is, I just logged into Roblox studio and realized that I was wrong. I’ll justify myself by saying that I wrote on my phone, not on the computer (and also that I have half a brain.)

I’ll bet on that. Personally, I define functions in this way very rarely, mostly utilitarian, but it’s not always more readable, moreover

--!strict

local function abc ()
	abc() -- good
end

local abc2 = function()
	print(abc2) -- nil
end

yes, this is fixed by adding one line, but I would call it a crutch.

as for the fact that you can just refer to a function, you’re right, it’s certainly more logical, but this nonsense doesn’t tell me anything, because it’s an obvious thing.

also, I want to say that this is a much less readable option in my opinion, at least you are violating the scope, which is not good. And well, my answer was 100% influenced by the fact that I was already used to writing “in the usual way” for a lon time

1 Like

readability doesnt metter; You make games for people to play with good enough FPS; Its not an art

1 Like