I have one question.
In this event, is the stat argument a string or object? I assume it’s a string, but I need to know to finish the script properly.
I have one question.
In this event, is the stat argument a string or object? I assume it’s a string, but I need to know to finish the script properly.
That event is indeed a string. Sorry for the late reply
Umm what is this suppos to be? BindToClose
only runs for 60 seconds and you should use it to save players’ data when game shut downs.
It’s okay.
This should be a function and complete rewritten version. Please let me know if something doesn’t work, because everything does when I test it.
In the code, the GetDataEvent is a RemoteEvent. I changed it to work with a RemoteFunction, so it can actually return the data to the client. In the events folder in ReplicatedStorage, delete the GetDataEvent, and then add a RemoteFunction to the folder. Name the RemoteFunction the exact same as the RemoteEvent was, so “GetDataEvent”.
Using a RemoteFunction is better than receiving something from the RemoteEvent, processing stuff, then firing it back to the client. It’s way more efficient to use RemoteFunctions because it returns it right back to the client, and I’m pretty sure using the RemoteEvent like that can cause some issues as well. I might be wrong on that though.
When using a RemoteFunction, you have to write it in a variable like a function in the script returning some data, like a number formatter or something.
Here is an example of what it would look like on the client:
local replicatedStorage = game:GetService("ReplicatedStorage")
local button = script.Parent
button.Activated:Connect(function()
local availableMaps = replicatedStorage.GetMaps:InvokeServer()
print(availableMaps) --// The server code for this example would find a folder in ServerStorage, get all of its children, and return that table. This would print a table with the names of all of the maps in that folder. This is really good to use so exploiters can't mess with maps in ReplicatedStorage. The client will be able to receive the maps from the server, because pretty much every exploiter can't really see anything that the server can, which includes ServerScriptService and ServerStorage
end)
-- >>: Variables
local Players = game:GetService("Players")
local replicatedStorage = game:GetService("ReplicatedStorage")
local dataStoreService = game:GetService("DataStoreService")
local marketplaceService = game:GetService("MarketplaceService")
local httpService = game:GetService("HttpService")
local dataStore = dataStoreService:GetDataStore("DataStore")
local events = replicatedStorage:WaitForChild("Events")
local gamepassIDs = {
DoubleIncrement = 154681682;
}
local defaultTable = {
Donated = 0;
Raised = 0;
Minutes = 0;
}
-- >>: Functions
--// Set Data
local function AddDonated(userId: number, amount: number)
local player = Players:GetPlayerByUserId(userId)
if player and player:FindFirstChild("leaderstats") then
player.leaderstats.Donated.Value += amount
end
end
local function AddRaised(userId: number, amount: number)
local player = Players:GetPlayerByUserId(userId)
if player and player:FindFirstChild("leaderstats") then
player.leaderstats.Raised.Value += amount
end
end
local function AddMinutes(userId: number, amount: number)
local player = Players:GetPlayerByUserId(userId)
if player and player:FindFirstChild("leaderstats") then
player.leaderstats.Minutes.Value += amount
end
end
--// Remotes
local function GetData(player: Player, stat, value)
for _, playerStat in pairs(player:GetDescendants()) do
if playerStat.Name == stat then
return playerStat, value
end
end
end
local function AddValue(userId: number, amount: number, stat: string)
if stat == "Donated" then
AddDonated(userId, amount)
elseif stat == "Raised" then
AddRaised(userId, amount)
elseif stat == "Minutes" then
AddMinutes(userId, amount)
end
end
--// Player Data
local function PlayerAdded(player: Player)
-- // Stats
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local donated = Instance.new("IntValue")
donated.Name = "Donated"
donated.Value = defaultTable.Donated
donated.Parent = leaderstats
local raised = Instance.new("IntValue")
raised.Name = "Raised"
raised.Value = defaultTable.Raised
raised.Parent = leaderstats
local minutes = Instance.new("IntValue")
minutes.Name = "Minutes"
minutes.Value = defaultTable.Minutes
minutes.Parent = leaderstats
--// Loading Data
local encoded
local dataTable
local success, errorMessage = pcall(function()
encoded = dataStore:GetAsync("dataTable-"..player.UserId)
end)
if success then
if encoded ~= nil then
dataTable = httpService:JSONDecode(encoded)
if dataTable ~= nil then
print("Successfully loaded data table for "..player.Name, dataTable)
else
warn("Data table for "..player.Name.." failed to be decoded")
dataTable = defaultTable
end
else
warn("There is no saved data table for "..player.Name)
dataTable = defaultTable
end
donated.Value = dataTable.Donated
raised.Value = dataTable.Raised
minutes.Value = dataTable.Minutes
else
if errorMessage then
warn(errorMessage, player.Name, player.UserId)
elseif encoded == nil then
warn("Data for "..player.Name.." is nil!", player.UserId)
end
dataTable = defaultTable
donated.Value = dataTable.Donated
raised.Value = dataTable.Raised
minutes.Value = dataTable.Minutes
end
--// Other Stuff
local minutesIncrement = 1
if marketplaceService:UserOwnsGamePassAsync(player.UserId, gamepassIDs.DoubleIncrement) then
minutesIncrement = 2
end
coroutine.wrap(function()
while task.wait(60) do
AddMinutes(player.UserId, minutesIncrement)
end
end)()
end
local function PlayerRemoving(player: Player)
local success, errorMessage = pcall(function()
local dataTable = {
Donated = player.leaderstats.Donated.Value;
Raised = player.leaderstats.Raised.Value;
Minutes = player.leaderstats.Minutes.Value;
}
local encoded = httpService:JSONEncode(dataTable)
dataStore:SetAsync("dataTable-"..player.UserId, encoded)
end)
if success then
print("Successfully saved data for "..player.Name, player.UserId)
else
warn(errorMessage)
end
end
-- >>: Events
--// Players Added/Removing
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(PlayerRemoving)
--// Remotes
events:WaitForChild("GetDataEvent").OnServerInvoke = GetData
events:WaitForChild("AddValue").Event:Connect(AddValue)
-- >>: Bind To Close
game:BindToClose(function()
task.wait(75)
end)
Hover your mouse over it and tell me what it says
It says this right here
Weird. I don’t know why that happens. I would switch it out with player:FindFirstChild("leaderstats")
for those three lines
That should have fixed it, but I’m not sure why it didn’t. I know it is probably do do something with the player
parameter being declared as the class “Player” given by the error. It should still run fine with that error, but if you want to try getting rid of it, remove the : Player
after the “player” parameter in the function.
Did you replace it with a RemoteFunction, or is it still a RemoteEvent?
It is still a remotevent, does it need to be a function?
Yes, it needs to be a RemoteFunction.
After the change it is still giving me the error
It should be working just fine. It works for me perfectly.
On your client, how are you running the code for it? It is requires to use it on the client, just like a RemoteEvent when firing the server, but it has to be setup slightly different.
Oh, and if you want to make your life easier, I really recommend this plugin. It’s super helpful
As in client do you mean change this?
No. The script with the DataStore should be a server script with either the run context set to Legacy or Server. I mean client as in there is supposed to be code somewhere that runs the event properly. Wherever it is that gets invoked, please show me.
As well as show me a screenshot of the properties of the RemoteFunction so I can see if something might be wrong there.
Where would the script most likley be? Workspace, SSS?