So, I already have one and am pretty happy with how it functions for the most part, however the issue is that it replicates itself to the client side and uses RemoteEvents to determine how much progress is being made. The downside to this is that the player can just duplicate the progress part, or they can fire the RemoteEvent and give themselves infinite progress.
Excuse my horrible naming haha. These scripts are replicated into each progress block.
local part = script.Parent -- Part the will activate when touched
part.Touched:Connect(function(hit)
local humanoid = hit.Parent:FindFirstChildOfClass("Humanoid")
if humanoid then
local char = hit.Parent
local player = game.Players[char.Name] -- This gets the player in game.Players by getting the name from its character
local plrid = player.UserId
local rs = game:GetService("ReplicatedStorage")
local ap = rs:WaitForChild("AddProgress"..plrid) -- the exploitable event
if script.Parent.Parent.Name == "Progress"..plrid then
ap:Fire(0.00512820512) -- amount of progress to give the player
script:Destroy() -- destroys the script for that individual user
end
end
end)
You should handle everything on Server and do not trust Client with things such as this! You can use Module Scripts to get basically the same effect, but safe.
You should handle this on the server, we can achieve this by have a script that manages all the parts you want to give progress. You should start by instancing a Folder into the workspace and then adding the parts into the folder.
You then want to place an IntValue into the part so you can easily set the progress each part will give. In the server script we will have a loop that add the part to an empty table, then we can set this part equal to an empty table, which will give you a dictionary. The instance is your key and your table is the value for the corresponding key.
We then can add the names of the player who has touched the part, and if you find the name inside of the table if they touch the part again it will not execute.
If you were to continue to use the client, which I highly recommend you don’t you should keep a table of value which have a key of the instance, the value of the key will be the amount of progress. You would only send the part inside of the parameters for your event.
local ProgressParts = -- Path to folder
local PartTable = {}
-- Setup table
for index , part in ipairs(ProgressParts:GetChildren()) do
if part:IsA("BasePart") then
table.insert(PartTable,part) -- Adds instance to table
PartTable[part] = {} -- Sets key to blank table
end
end
for index , part in ipairs(PartTable) do
part.Touched:Connect(function(Hit)
local Player = game:GetService("Players"):GetPlayerFromCharacter(Hit.Parent)
local ProgressValue = part:FindFirstChild("NAME_OF_VALUE") -- IntValue for progress
if Player then
if not table.find(PartTable[part],Player.Name) then
table.insert(PartTable[part],Player.Name) -- Adds name into table
print(table.unpack(PartTable[part])) -- For debugging, this just prints all contents of the table
-- Add progress here
end
end
end)
end
To be fair you’re not wrong about using a module, but I would suggest to just have a table with the set values or my method with IntValues which are checked on the server, this way you can have one script handling the values, the touched event, and data.
There will be no need to require a module nor risk their table being tampered with if he had the module inside replicated storage.
Was thinking of using a table actually, wasn’t sure how to go about it though. Could I just put this in each part and then give each part its own table instead, and then check if the player has touched it? I feel like it would take up a lot of bandwidth though. What do you think?
I feel like it would be easier to manage for myself. Also, I could just do like 1 / #ProgressFolder:GetChildren() to determine how much progress to give, as that’s basically the formula I was using before anyway. I’d use your method but maybe modified a bit as well.