Alright guys so I made a voting system, yesterday there was a bug which I asked for help about but I had to fix it myself eventually. Anywho this is the code do you guys think it’s fine?
-- Set the attribute when the player joins. (Server script)
game.Players.PlayerAdded:Connect(function(player)
player:SetAttribute("Game1Votes", 0)
player:SetAttribute("Game2Votes", 0)
end)
-- Client voting
local Player = game.Players.LocalPlayer
local PlayerGui = Player.PlayerGui
local RE = game.ReplicatedStorage:WaitForChild("CalculateVotes")
local Game1 = PlayerGui:WaitForChild("ScreenGui"):WaitForChild("Game1")
local Game2 = PlayerGui:WaitForChild("ScreenGui"):WaitForChild("Game2")
Game1.MouseButton1Click:Connect(function()
local Game1Votes = Player:GetAttribute("Game1Votes")
if Game1Votes and Game1Votes == 0 then
RE:FireServer("Game1Votes", 1, Game1)
end
local Game2Votes = Player:GetAttribute("Game2Votes")
if Game2Votes and Game2Votes == 1 then
RE:FireServer("Game2Votes", 0, Game2)
end
end)
Game2.MouseButton1Click:Connect(function()
local Game2Votes = Player:GetAttribute("Game2Votes")
if Game2Votes and Game2Votes == 0 then
RE:FireServer("Game2Votes", 1, Game2)
end
local Game1Votes = Player:GetAttribute("Game1Votes")
if Game1Votes and Game1Votes == 1 then
RE:FireServer("Game1Votes", 0, Game1)
end
end)
-- Add the client's votes to the server's total votes
local RepClients = game.ReplicatedStorage.ReplicateClients
local Calculations = game.ReplicatedStorage.CalculateVotes
local TotalGame1Votes = 0
local TotalGame2Votes = 0
Calculations.OnServerEvent:Connect(function(Player, ReceivedAttribute, Votes, ChosenGame)
if Player then
Player:SetAttribute(ReceivedAttribute, Votes)
print(ReceivedAttribute ..":" .. Player:GetAttribute(ReceivedAttribute))
if ReceivedAttribute == "Game1Votes" then
local Game1Votes = Player:GetAttribute(ReceivedAttribute)
if Game1Votes == 0 then
TotalGame1Votes -= 1
RepClients:FireAllClients(ChosenGame.Name, TotalGame1Votes)
end
if Game1Votes == 1 then -- This ensures hackers don't give themselves extra votes
TotalGame1Votes += Game1Votes
RepClients:FireAllClients(ChosenGame.Name, TotalGame1Votes)
end
end
if ReceivedAttribute == "Game2Votes" then
local Game2Votes = Player:GetAttribute(ReceivedAttribute)
if Game2Votes == 0 then
TotalGame2Votes -= 1
RepClients:FireAllClients(ChosenGame.Name, TotalGame2Votes)
end
if Game2Votes == 1 then -- This ensures hackers don't give themselves extra votes
TotalGame2Votes += Game2Votes
RepClients:FireAllClients(ChosenGame.Name, TotalGame2Votes)
end
end
end
end)
-- Replicating the changes to all clients now
local RepClients = game.ReplicatedStorage:WaitForChild("ReplicateClients")
local Player = game.Players.LocalPlayer
RepClients.OnClientEvent:Connect(function(ChosenGame, TotalVotes)
Player.PlayerGui:WaitForChild("ScreenGui"):WaitForChild(ChosenGame).Text = TotalVotes
end)
I assume there might be a better way to make to make a voting system however I don’t really use tutorials or anything and instead I try to do things myself. I’m happy it works though, I assume it couldn’t be exploited because I added a sanity check to ensure the player’s votes are either 0 or 1 and nothing above that. And it’s more optimized with attributes I think. But maybe I’m wrong. Let me know.
Also it’s your choice. I just gave an opinion on your code and your considering it like I am a very good and popular developer who called your code PATHETIC and your entire career depends on that one review. Stop being an attention seeker. Please.
The system would be more extendable if you replaced the two attributes with just one representing the players vote. Then once someone votes tally it all up and send it to the clients as a table.
Heres a template example of that, using functional programming:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local VoteRemote = ReplicatedStorage.Vote
local IsVotingActive = false
local VotingCache = {}
local function ProcessPlayerVote(player: Player, vote: string)
local userId = player.UserId
VotingCache[userId] = vote
end
local function ClearVotes()
table.clear(VotingCache)
end
-- This function will process all votes and return the most voted one
local function GetMostVoted()
local mostVotedString = ""
local mostVotedInt = 0
local votingCount = {}
for _, vote in VotingCache do
if not votingCount[vote] then
votingCount[vote] = 0
end
votingCount[vote] += 1
end
for voteString, voteCount in votingCount do
if voteCount > mostVotedInt and mostVotedString ~= voteString then
mostVotedInt = voteCount
mostVotedString = voteString
end
end
return mostVotedString
end
local function OpenVoting(lifeTime: number)
if IsVotingActive then
warn(`Voting is already active!`)
return
end
IsVotingActive = true
local voteRemoteConnection = nil
voteRemoteConnection = VoteRemote.OnServerEvent:Connect(ProcessPlayerVote)
task.wait(lifeTime)
-- Disable new votes
voteRemoteConnection:Disconnect()
local mostVoted = GetMostVoted()
ClearVotes()
-- do soemthing with that
IsVotingActive = false
end
-- Garbage collecting basically
Players.PlayerRemoving:Connect(function(player)
local userid = player.UserId
if VotingCache[userid] then
VotingCache[userid] = nil
end
end)
-- Example use
OpenVoting(5) -- opens voting for 5 seconds
As for the client, it should just be able to assume and track its own vote. But if you prefer you could fire a remote to the client confirming the vote went through
If you wanted to add further security, i.e. make sure only lets say 2 things can be voted on, do something like this:
local ValidVotes = {
"Something1",
"Something2"
}
local function ProcessPlayerVote(player: Player, vote: string)
if not table.find(ValidVotes, vote) then
warn(`{player.Name} attempted to vote for something invalid!`)
return
end
local userId = player.UserId
VotingCache[userId] = vote
end
Full script:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local VoteRemote = ReplicatedStorage.Vote
local ValidVotes = {
"Something1",
"Something2"
}
local IsVotingActive = false
local VotingCache = {}
local function ProcessPlayerVote(player: Player, vote: string)
if not table.find(ValidVotes, vote) then
warn(`{player.Name} attempted to vote for something invalid!`)
return
end
local userId = player.UserId
VotingCache[userId] = vote
end
local function ClearVotes()
table.clear(VotingCache)
end
-- This function will process all votes and return the most voted one
local function GetMostVoted()
local mostVotedString = ""
local mostVotedInt = 0
local votingCount = {}
for _, vote in VotingCache do
if not votingCount[vote] then
votingCount[vote] = 0
end
votingCount[vote] += 1
end
for voteString, voteCount in votingCount do
if voteCount > mostVotedInt and mostVotedString ~= voteString then
mostVotedInt = voteCount
mostVotedString = voteString
end
end
return mostVotedString
end
local function OpenVoting(lifeTime: number)
if IsVotingActive then
warn(`Voting is already active!`)
return
end
IsVotingActive = true
local voteRemoteConnection = nil
voteRemoteConnection = VoteRemote.OnServerEvent:Connect(ProcessPlayerVote)
task.wait(lifeTime)
-- Disable new votes
voteRemoteConnection:Disconnect()
local mostVoted = GetMostVoted()
ClearVotes()
-- do soemthing with that
IsVotingActive = false
end
-- Garbage collecting basically
Players.PlayerRemoving:Connect(function(player)
local userid = player.UserId
if VotingCache[userid] then
VotingCache[userid] = nil
end
end)
-- Example use
OpenVoting(5) -- opens voting for 5 seconds