I’ve been delving into the realm of anti-cheat systems on Roblox, and one challenge that caught my attention was establishing a seamless communication channel between the client and the server. In my quest to contribute to our community, I’ve come up with a nifty solution.
Script 1 (Client - LocalScript)
-- First, we access ReplicatedStorage to find our RemoteFunction.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction
-- We keep searching for the "Security" RemoteFunction until we find it.
local remoteFunction = ReplicatedStorage:WaitForChild("Security")
-- Define our input code and check interval.
local inputCode = "code1"
local checkInterval = 5
-- This function runs in the background, constantly checking for changes.
spawn(function()
while true do
wait(checkInterval)
local result = remoteFunction:InvokeServer(inputCode)
if result == "code2" then
-- Execute your code when the correct code is received.
end
end
end)
Script 2 (Server - Script)
-- We start by accessing ReplicatedStorage.
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Create a new RemoteFunction and name it "Security".
local remoteFunction = Instance.new("RemoteFunction")
remoteFunction.Name = "Security"
remoteFunction.Parent = ReplicatedStorage
-- Define our secret code.
local secretCode = "code1"
-- This function handles the server's response to client requests.
remoteFunction.OnServerInvoke = function(player, inputCode)
if inputCode == secretCode then
return "code2"
else
return false
end
end
In a nutshell, these scripts enable seamless communication between the client and server, with Script 2 acting as the gatekeeper, allowing access only when the correct code is provided.
Of course, this is just one approach. I’ve got plenty of other ideas up my sleeve, such as creating another Server-Script to clone the LocalScript into a player’s backpack at specific intervals, and more. The possibilities are endless!
Wishing you the best of luck with your scripts and happy coding!
What exactly is the purpose for this script? It isn’t exactly self-explanatory, it seems to simply just transfer codes between the server and client as a sort of “heartbeat system” which while helpful in the combat of exploiters, it isn’t necessarily a “secure connection” since a client could simply write their own script to replicate the heartbeat system.
WaitForChild would possibly be more appropriate here?
The script’s primary purpose is to establish a secure line of communication between the client and server in the context of Roblox game development. Think of it as a secret code exchange. The server and client need to exchange specific codes to authenticate each other.
Now, you rightly pointed out that a client could attempt to replicate this system. However, this is where the clever part comes in. The “code1” and “code2” elements in the script act like secret keys or handshakes. The client cannot successfully replicate the system unless they possess the correct “code1.” It’s like trying to open a vault without knowing the combination.
Regarding the use of a repeat loop with wait instead of WaitForChild, it’s a deliberate choice. This approach ensures that the client continuously checks for the existence of the RemoteFunction named “Security.” Even if the server changes the name or creates it later, the client script remains adaptable and effective.
In essence, this script provides a foundational level of security, akin to locking the front door of a house. However, just like you can add more locks and security measures to a door, you can build upon this foundation with additional layers of security to make it even more challenging for potential exploiters.
:WaitForChild, like :FindFirstChild and the simple . operator returns the instance itself. The variable does not become nil after it changes names:
local MyPart = workspace:WaitForChild("Part")
MyPart.Name = "NotAPart"
-- MyPart is still a reference to workspace.Part, or rather, workspace.NotAPart
-- We can still use it even though the name has changed
print(MyPart.Name)
The RemoteFunction is dynamically created:
While the server will logically always start running server scripts before the client starts running local scripts, latency means that :WaitForChild should always be used when trying to reference objects created by Instance.new. Static objects, i.e. objects in the Studio Explorer before the game even runs, are always going to exist on the client however (respecting the Server... objects ofc).
The client knows everything your localscripts know, because your localscripts run on the client. Exploiters have a tool called “RemoteSpy”, and it lets them view RemoteEvents or RemoteFunctions that are fired or invoked. An exploiter could just get the secret key from there.
This script doesn’t make a ton of sense. inputCode never changes on the client, the server-script does nothing when the inputCode is incorrect, and at the end of the day the localscript can just be modified to get rid of the
if result == "code2" then
check.
I’m not trying to discourage you from trying to contribute to the community, I hope my criticism will lead you into learning more about programming on Roblox to become a better coder.
You know exploiters have access to the debug library, right? https://www.lua.org/manual/5.1/manual.html#lua_getupvalue
A “secret” code is meaningless if an exploiter can just find it by, for example, hooking any function in that script and getting its upvalues.
This is why developers recommend to not trust the client.
Even if you managed to make something hard to crack, you’re fighting not against one exploiter, but against thousands, if not more. It will generally get bypassed in less time than it took you to create the anticheat (or at least fast enough that it’s not worth the effort).
It’s simply not cost-effective to implement client-sided anticheats unless you have people on your team whose sole purpose is doing that.
You’re right; exploiters can access the debug library in Lua. However, the “secret” code in the script serves as a layer of security. While it’s not foolproof, it adds a barrier that exploiters need to overcome. The script aims to make exploiting more challenging, not impossible.
The script is just one part of a multi-layered anti-exploit strategy. While it’s true that some exploiters are persistent, implementing client-side anti-cheat measures is still worthwhile. It deters a significant number of potential exploiters and can buy time for server-side anti-cheat mechanisms to kick in.
In the end, a robust anti-exploit system involves a combination of client and server-side measures. While it may not stop all exploiters, it can significantly reduce the number of successful exploits and improve the overall gaming experience for legitimate players.
Using a repeat loop with wait in the script to find the “Security” RemoteFunction was a deliberate choice. While we could have used :WaitForChild() for a quicker wait, we decided on the repeat loop to make sure the client consistently checks for the RemoteFunction’s existence. This way, even if the RemoteFunction’s name changes or it’s created later, the client script won’t miss it. It’s about being adaptable to changes in the server’s setup.
Now, about creating the RemoteFunction dynamically with Instance.new(), you’re right that :WaitForChild() is usually recommended to avoid latency issues. However, in this specific script, our main concern was ensuring that the client keeps searching for the RemoteFunction. While latency is important, maintaining a reliable reference to the RemoteFunction was crucial in this situation.
You’ve raised a valid point regarding potential exploiters trying to mess with client-side code and using tools like “RemoteSpy” to intercept secret keys. It’s important to note that this script isn’t a complete anti-exploit solution. It acts as a foundational layer of defense. To enhance security, we’d need additional layers of protection and obfuscation to make it harder for exploiters to manipulate the system.
You might be confused, since I’m not American and my native language is not English, I use DeepL to communicate or to correct any grammatical mistakes I make from time to time.
And yes, I know RemoteSpy, but as I said 3 times here, the script I sent you is just a starting point.
Thank you for sharing your expertise. While this script isn’t foolproof, it’s a starting point for those looking to enhance security.
I took a look at your thread, see if you are now satisfied:
LocalScript:
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction
repeat
remoteFunction = ReplicatedStorage:FindFirstChild("Security")
wait(0.7)
until remoteFunction
local inputCode = "code1"
local checkInterval = 5
spawn(function()
while true do
wait(checkInterval)
local success, result = pcall(function()
return remoteFunction:InvokeServer(inputCode)
end)
if success then
if type(result) == "string" and result == "code2" then
print("Received code2 from the server.")
end
end
end
end)
Script 2 (Server):
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local remoteFunction = Instance.new("RemoteFunction")
remoteFunction.Name = "Security"
remoteFunction.Parent = ReplicatedStorage
local secretCode = "code1"
local playerRequestTimes = {}
local maxRequestInterval = 5
remoteFunction.OnServerInvoke = function(player, inputCode)
local currentTime = tick()
local lastRequestTime = playerRequestTimes[player]
if lastRequestTime and (currentTime - lastRequestTime) < maxRequestInterval then
return false
end
playerRequestTimes[player] = currentTime
if type(inputCode) == "string" and inputCode == secretCode then
return "code2"
else
return false
end
end
What you could do though is use encryption (ex AES-CBC) and StepNumbers, that would be the very least in terms of security,
But if you want to take it a step further, make a client anticheat in addition to your server one and add Anti HookMetaMethod, Overflow Checks, and even CoreGui checks which are all easy to make (from my perspective)
Here you are misleading new developers by using ChatGPT to get easy likes, however what you are actually doing is making yourself look inexperienced and making other’s game security weaker and easier to attack for exploiters.
I was talking about handshakes, + you are still not using :WaitForChild and you are still not using the task library which is way more optimized than spawn and wait.
This is an unoptimized method, please use :WaitForChild when possible as it is written in C++ (not lua) and is essentially the same thing as you are doing, except it will be way faster.