Yes it is now LOL but now I’m getting this erorr.
^ This is line 86.
Yes it is now LOL but now I’m getting this erorr.
^ This is line 86.
You need to have a “default condition” if the player has no saved data. Something like this:
if NotepadText then
TextLabel.Text = NotepadText
else
TextLabel.Text = "Write something here"
end
I have a placeholder text that says start taking your first note so I decited to do this instead…
if NotepadText then
TextLabel.Text = NotepadText
end
So that way the TextBox’s Text is empty.
Anyways, the text I put into the TextBox won’t save, with no errors, maybe you can see what’s up?
LocalScript (a bit of code has been sniped out):
local SaveData = RemoteFunction:InvokeServer(false)
if SaveData then
TextBox.Text = SaveData
end
ServerScript:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local NotepadData = DataStoreService:GetDataStore("NotepadData")
local RemoteFunction = ReplicatedStorage.Events.RemoteFunctions.NotepadData
local LoadedDataBindable = Instance.new("BindableEvent")
local SaveData = {}
local LoadedData = {}
function PlayerAdded(Player)
local Success, Data = pcall(function()
return NotepadData:GetAsync("User_"..Player.UserId)
end)
if Success then
SaveData[Player] = Data
end
if not Success then print(Data) end
LoadedData[Player] = true
LoadedDataBindable:Fire()
end
Players.PlayerAdded:Connect(PlayerAdded)
RemoteFunction.OnServerInvoke = function(Player, Save, Data)
while not LoadedData[Player] do
LoadedDataBindable.Event:Wait()
end
end
Don’t mean to sound rude, but I was under the impression you were more familiar with Roblox Lua or programming in general. That’s okay, I’m fine holding your hand through this problem — others will be reading this in the future as well, and may have the same kinds of questions.
You left the remainder of this function empty:
RemoteFunction.OnServerInvoke = function(Player, Save, Data)
while not LoadedData[Player] do
LoadedDataBindable.Event:Wait()
end
end
In my first follow-up post, I put this here just so I wasn’t copy-pasting a bunch of code from my initial post:
In actuality, it would look like this:
RemoteFunction.OnServerInvoke = function(Player, Save, Data)
while not LoadedData[Player] do
LoadedDataBindable.Event:Wait()
end
if Save == true then
-- If the player called the remote to SAVE their data
--[[ Make sure you sanitize the data before this point so exploiters
can't fill up your data stores with junk ]]
SaveData[Player] = Data
pcall(function()
DataStore:SetAsync(Player.UserId, Data)
end)
elseif Save == false then
-- If the player called the remote to RETRIEVE the server's data
return SaveData[Player]
end
end
If you used the above code for your Remote.OnServerInvoke
function, it would work.
This server code is not finished, however. Note this comment:
It would not be “secure” — a remote exploiter could choose to spam that remote with giant 4 megabyte strings and throttle the server’s data store usage.
Prior to these lines,
make sure you have some cancel conditions if they’re firing the remote too fast or they’re sending through notepad text which is obviously way too long. You’ll need to set some parameters here. Here is how you “cancel” a function:
if 2 + 2 == 5 then return end
print("Test")
Test
is not printed because the code was “canceled” with a blank return before that print line could be reached. The code was “canceled” because 2+2 isn’t 5.
If this is all still confusing to you, I can try to elaborate further. I would like to see what methods you try though.
Sorry, I specialize in the UI side of coding on Roblox, not so much game functionality hence why I’m making a notepad.
I did notice though, my LocalScript does not save the data when the player leaves. So I added this.
game.Players.PlayerRemoving:Connect(function(Player)
RemoteFunction:InvokeServer(true)
end)
Hence this part
if Save == true then
SaveData[Player] = Data
pcall(function()
NotepadData:SetAsync("User_"..Player.UserId, Data)
end)
is for when the player calls the remote to save their data, though it still won’t work. I’m losing my mind lol
I’ve never tested this before. I wonder if it would run if you left the game, since it’s a question of which code gets run first — your PlayerRemoving
function or Roblox’s internal script cleanup?
I would not rely on this method. I would just Remote:InvokeServer(true, notepadText)
every time I changed the notepad text, and then have the server only run SetAsync when its own PlayerRemoving
function is called, using its SaveData
table.
In other words, the client cares enough to tell the server every time it makes changes to the notepad text, but the server only cares enough to tell Roblox’s data stores when you leave the game.
Running SetAsync
on every remote call is not an efficient use of data stores, even though that’s what I put in the code before. I would recommend to you and any other readers that the server only call Roblox’s data store functions when players join or leave the game, and also perhaps on the server’s own auto saving schedule.
Oh, also you aren’t checking if this Player
is the LocalPlayer
:
There’s also DataStore2, which I’ve never used or examined in any way, but it’s worth a shot if it helps make this less of a headache.
I didn’t know I had to include the TextBox’s text, all I had was Remote:InvokeServer(true)
.
Oh alright, this should do the trick then.
game.Players.PlayerRemoving:Connect(function(Player)
local LocalPlayer = game.Players.LocalPlayer
if Player.Name == LocalPlayer.Name then
RemoteFunction:InvokeServer(true, TextBox.Text)
end
end)
Gotcha, I’ll look into it.
You have to send the text because the server has to save it. How else would the server know what to save?
Lol, you’re right, sorry. I’m getting a new error were the TextBox’s Text gets set to the Data in the localscript
Print it to debug whats happening
Gotcha. I did a little more work this morning and I found out that my DataStore Key looks like this
How would I go about loading and saving the actual string, instead of that whole table, since it looks like that’s what my error was above
Print the Data
variable on the server right before you save it so we can see what it is. something tells me it’s not actually the notepad text
In the last few hours I’ve been trying to figure out how to use DataStores and get an actual understanding of them lol, and I’ve finally got a working script. I changed your code around a bit, added stuff and removed stuff such as a SavedData table you had, and I added a RemoteEvent to send the Data over.
If you think I should edit some stuff or change some stuff let me know!
Client (some parts have been cut out, but this is the jest):
local NotepadData = ReplicatedStorage.Events.RemoteFunctions.NotepadData
local NotepadSetText = ReplicatedStorage.Events.RemoteEvents.NotepadSetText
local TextBox = script.Parent.TopbarUIs.Notes.Main.Container.TextBox
NotepadSetText.OnClientEvent:Connect(function(Data)
if Data then
TextBox.Text = Data
end
end)
game.Players.PlayerRemoving:Connect(function(Player)
local LocalPlayer = game.Players.LocalPlayer
if Player.Name == LocalPlayer.Name then
NotepadData:InvokeServer(true, TextBox.Text)
print("(client) "..LocalPlayer.Name.." left, client invoked server to save data: "..TextBox.Text)
end
end)
-- ^^ not to sure if this works yet. vv this below will change to 60s and not 10s
spawn(function()
while wait(10) do
NotepadData:InvokeServer(true, TextBox.Text)
print("(client) AUTO SAVE, attempted to send over data to server from client: "..TextBox.Text)
end
end)
Server:
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")
local NotepadData = DataStoreService:GetDataStore("NotepadData")
local RemoteFunction = ReplicatedStorage.Events.RemoteFunctions.NotepadData
local RemoteEvent = ReplicatedStorage.Events.RemoteEvents.NotepadSetText
local SavedData = ""
Players.PlayerAdded:Connect(function(Player)
local Success, Data = pcall(function()
return NotepadData:GetAsync("User_"..Player.UserId)
end)
if Success then
RemoteEvent:FireClient(Player, Data)
print("(server) successfully fetched data. data fired to client via remote")
end
end)
RemoteFunction.OnServerInvoke = function(Player, State, Data)
if State == true then
SavedData = Data
local Success, Data = pcall(function()
NotepadData:SetAsync("User_"..Player.UserId, Data)
end)
if Success then
print("(server) SUCCESSFULLY SAVED DATA")
end
elseif State == false then
print("(server) is sending over data to client")
return SavedData
end
end
I temporally (commented it out) removed this part to see if the game.Players.PlayerRemoving part would work and it didn’t. Do you or anyone else know how I would save the data when the player leaves?
Would you know how I could solve this or if anyone else could?
Don’t use the client PlayerRemoving thing. Do this:
I’ve thought about it but this fires way to many times
In the server’s RemoteFunction call, only fill in the SaveData table.
Not sure if you saw my recent script but I ended up removing the SaveData table. I’ve been seeing how I should save the data on the server so I came up with a decent method though it won’t save (text updates and loads the data saved though, the server script just does not save the text)
Client:
NotepadSetText.OnClientEvent:Connect(function(Data)
if Data then
TextBox.Text = Data
if game.Workspace:FindFirstChild(LocalPlayer.Name) then
game.Workspace[LocalPlayer.Name]:SetAttribute("NotepadData", Data)
end
end
end)
TextBox:GetPropertyChangedSignal("Text"):Connect(function()
if game.Workspace:FindFirstChild(LocalPlayer.Name) then
game.Workspace[LocalPlayer.Name]:SetAttribute("NotepadData", TextBox.Text)
end
end)
Server:
game.Players.PlayerRemoving:Connect(function(Player)
local Character = Player.Character or Player.CharacterAdded:Wait()
local CharacterNotepadData = Character:GetAttribute("NotepadData")
pcall(function()
NotepadData:SetAsync(Player.UserId, CharacterNotepadData)
end)
end)
If the server’s remote code is only setting an attribute, then the data store throttling from this post shouldn’t be happening.
Also, set the attribute on the player instance, not the character, since you don’t know if the character exists when the PlayerRemoving
event fires.
Would you like me to show you an example of a very simple thing I made a while ago which follows the rules I posted about here?