Saving moved data inside a folder, could anyone please help?

Hey there,

I’m kind of desperate and running out of options here. My problem in a nutshell, is this;
I have a folder named ‘QuestsReplicated’ inside the ReplicatedStorage.

DevScreen1

This folder is filled with quests (in the form of BoolValues), and these quests have values inside them like the recommended level, reward and objective. The objective is the only one that’s important for now.

Once a quest giver has given a quest, the quest Boolvalue (along with all of it’s children values) will be cloned to the player folder named ‘Quests’. From here on the objective changes depending on what the player does (like killing an enemy or collecting 10 logs of wood).

In the end, when a quest is finished, the BoolValue turns to true, and the objective value says:
“Congratulations! You’ve completed this quest!”

What the problem is, is that I don’t know how to save these completed quests inside the ‘Quests’ folder to the datastore. What I’m looking for is that the quest stays inside the ‘Quests’ folder when a player joins.

Any help would be greatly appreciated, if I’m being unclear - feel free to ask for more clarification, and I’ll happily give it to you.

Thanks in advance!

You store a dictionary where each key is the quest’s name set to the message you need to display. When you load the data, recreate the Quests folder (they already are inside of ReplicatedStorage), loop through the retrieved dictionary (:GetAsync()), and each time do Quests[key].Objective = value where key is the current quest name in the dictionary, and value is the message that quest is set to. This is an example of how you can structure the dictionary.

{LumberJacking = "Bring the 10 logs of wood to Tobias the farmer."}
1 Like

Hey there. Thanks for the reply!

I’ve been trying to solve this all day, so I’m gonna take a little break until tomorrow. I appreciate the response, and I really hope that this is the key to my problem. If I have any questions I’ll be sure to ask them next morning.

Have a great day/evening/morning/night! :slightly_smiling_face:

1 Like

Hey there, @starmaq

I just started making the dictionary and writing the script. Here’s what I have so far:

local DataStoreService = game:GetService("DataStoreService")
local PlrService = game:GetService("Players")
local Storage = DataStoreService:GetDataStore("PlayerData")

local function onPlayerRemoving(plr)
	local quests = {
		["Lumberjacking"] = plr.Quests["Lumberjacking"].Objective.Value,
		["Doppelsoldner"] = plr.Quests["Doppelsoldner"].Objective.Value,
		["Courtship"] = plr.Quests["Courtship"].Objective.Value
		
	}
	
	local success, err = pcall(function()
		Storage:SetAsync(plr.UserId, quests)
	end)
	
	if success then
		print("Success!")
	end
end

PlrService.PlayerRemoving:Connect(onPlayerRemoving)

local function onPlayerAdded(plr)
	
end

PlrService.PlayerAdded:Connect(onPlayerAdded)


--for i = 1, #dictionary do
-- insert code here
--end

My question is; how would I efficiently check if the quest is inside the player’s quest folder? And how should I ‘recreate’ the quests folder?

Thanks!

Hay there. You would recreate the folders as if you’re recreating leaderstats. You first load the data so you know which quests the player has, and for each quest create a StringValue inside the quests Quests folder, the name being the current key. And inside each StringValue create 4 StringValues, and put inside of them the data you saved.

To check if a quest is inside the player you simply do

if player.Quests:FindFirstChild("quest name") then

It’s also good practice to do

local quest = player.Quests:FindFirstChild("quest name")

if quest then

since you’d probably be using the quest later.

1 Like

Hey again,

Thanks so much for your help and responding - you’re really helping me on the right track and I appreciate that :slightly_smiling_face: I’ve got a few more questions:

How would I get the quest dictionary when doing ‘Storage:GetAsync’ (inside the onPlayerAdded function)?

local PlrService = game:GetService("Players")
local Storage = DataStoreService:GetDataStore("PlayerData")

local function onPlayerRemoving(plr)
   if plr.Quests:FindFirstChildOfClass("BoolValue") then
   	local quests = {
   		["Lumberjacking"] = game.ReplicatedStorage.QuestsReplicated["Lumberjacking"].Objective.Value,
   		["Doppelsoldner"] = game.ReplicatedStorage.QuestsReplicated["Doppelsoldner"].Objective.Value,
   		["Courtship"] = game.ReplicatedStorage.QuestsReplicated["Courtship"].Objective.Value
   	}
   	
   	local success, err = pcall(function()	
   		Storage:SetAsync(plr.UserId, quests)
   	end)
   
   if success then
   	print("Success!")
end
end
end

PlrService.PlayerRemoving:Connect(onPlayerRemoving)

local function onPlayerAdded(plr)
   Storage:GetAsync(plr.UserId, quests) -- kinda clueless here
end

PlrService.PlayerAdded:Connect(onPlayerAdded)


--for i = 1, #dictionary do
-- insert code here
--end

And how would I make an effecient if statement to check if the player has the quest in his folder? Because whenever I try to do something like that, the script no longer recognizes the quest variable, so to speak.

	if plr.Quests:FindFirstChild("Lumberjacking") then
		local quests = {
			["Lumberjacking"] = game.ReplicatedStorage.QuestsReplicated["Lumberjacking"].Objective.Value,
			["Doppelsoldner"] = game.ReplicatedStorage.QuestsReplicated["Doppelsoldner"].Objective.Value,
			["Courtship"] = game.ReplicatedStorage.QuestsReplicated["Courtship"].Objective.Value
		}
		
	end
		local success, err = pcall(function()
			if plr.Quests:FindFirstChild("Lumberjacking") then
				local lumberJacking = quests[Lumberjacking]
			end
	
			Storage:SetAsync(plr.UserId, lumberJacking)
		end)
	
	if success then
		print("Success!")
	end
end`

Again, thanks alot for wanting to help me!

you get all quests like this:

local y = Storage:GetAsync(plr.UserId, quests)
-- create quest objects
for i,v in pairs(y) do
 local x = Instance.new("StringValue")
 x.Name = i -- Lumberjacking (name of quest)
 x.Value = v -- Objective (the objective)
end

I don’t quite know what you mean by a more efficient if statement, could you clarify that please?

1 Like

Hey there!

Thanks, that will probably prove helpful :grinning:

What I meant was that the script forgets about the variable set after the if statement because of an ‘end’ shortly afterwards.

I’ve got a pretty busy evening ahead of me, so I’ll jump back into the scripting tomorrow, thanks for the reply and wanting to help me! Have a great evening.

Hey there, lads

Alright so, I think this might be the final obstacle in the script.

Thanks to you folks I managed to figure out how to get all the quests, recreate them and loop through them. Thanks for that! However, I’m still very puzzled on how I should detect what quests the player has in his folder.

The obvious answer to this would be to do :FindFirstChild(), but how would I save that in :SetAsync?

If I’m being unclear, feel free to ask for more clarification. Thanks for the help!

local DataStoreService = game:GetService("DataStoreService")
local PlrService = game:GetService("Players")
local Storage = DataStoreService:GetDataStore("PlayerData")

local quests = {
	["Lumberjacking"] = game.ReplicatedStorage.QuestsReplicated["Lumberjacking"].Objective.Value,
	["Doppelsoldner"] = game.ReplicatedStorage.QuestsReplicated["Doppelsoldner"].Objective.Value,
	["Courtship"] = game.ReplicatedStorage.QuestsReplicated["Courtship"].Objective.Value
}

local function onPlayerRemoving(plr)
	if plr.Quests:FindFirstChild("Lumberjacking") then
			-- what should I put here?
	end
	
	if plr.Quests:FindFirstChild("Doppelsoldner") then
		
	end
	
	if plr.Quests:FindFirstChild("Courtship") then
		
	end
	
		local success, err = pcall(function()	
			Storage:SetAsync(plr.UserId, quests)
		end)
	
	if success then
		print("Success!")
end
end

PlrService.PlayerRemoving:Connect(onPlayerRemoving)

local function onPlayerAdded(plr)
	Storage:GetAsync(plr.UserId, quests)
end

PlrService.PlayerAdded:Connect(onPlayerAdded)

I am so lost with that code, basically what I am reading right now:

You got datastore service
a table
a function that fires when player is removed which is indexing Quests…

Then I notice you retrieve the data on player added and don’t even store that data anywhere…

Basically I’d do something like this if I were you…

local ds = game:GetService("DataStoreService"):GetDataStore('dssss')
local data = {}
game.Players.PlayerAdded:Connect(function(p)
    if ds:GetAsync(p.UserId) then
        data[p.Name] = ds:GetAsync(p.UserId)
    else
        data[p.Name] = {}
    end
end)
game.Players.PlayerRemoving:Connect(function(p)
    ds:SetAsync(p.UserId, data[p.Name])
end)
-- with that being said you can add folders inside... also I strongly suggest using bind to close
Quests.Something:Connect(function() -- when you want to change data
    data[p.Name]["Lumberjacking"] = 10
end)

You can basically iterate through this stuff, pretty much the neatest way of using datastores.

1 Like

Hey there!

Yes, the code is very messy, I’m very unexperienced with data stores.
Using your code, how would I detect what quests the player has inside of his folder? And how would I recreate the quests as mentioned above?

Thanks for the reply.

It is all done for you in that single table, the new table will load when the player joins and save the old table when the player leaves, you can get each data by indexing the portions, here is an example:

local data = {["souflee"]={["Lumberjacking"] = 10}}

basically a folder without being visible, if you would like it to be visible then you can iterate through the table and create a new value based on it

Oh alright! That sounds logical.

What should this function look like?

Quests.Something:Connect(function()
    data[p.Name]["Lumberjacking"] = 10
end)

I also read that isn’t a good idea to use the player name in a datastore (considering players can change their username), is there an alternative for this?

We are saving using their userid, so even if they do change their name, it will be the same name throughout the game, there will be no harm done to the data.

Also the function should be for when you want to change the data

1 Like

Thanks alot for wanting to help me in the right direction, I appreciate it! :slightly_smiling_face:

I’m still quite puzzled, and this code is still probably very messy - what should be edited for it to work?

local DataStoreService = game:GetService("DataStoreService")
local PlrService = game:GetService("Players")
local Storage = DataStoreService:GetDataStore("PlayerData")

local data = {}

local quests = {
	["Lumberjacking"] = game.ReplicatedStorage.QuestsReplicated["Lumberjacking"].Objective.Value,
	["Doppelsoldner"] = game.ReplicatedStorage.QuestsReplicated["Doppelsoldner"].Objective.Value,
	["Courtship"] = game.ReplicatedStorage.QuestsReplicated["Courtship"].Objective.Value
}

PlrService.onPlayerAdded:Connect(function(plr)
	if ds:GetAsync(p.UserId) then
		data[p.Name] = ds:GetAsync(p.UserId)
	else
		data[p.Name] = {}
	end
end)

PlrService.PlayerRemoving:Connect(function(plr)
	
	local success, err = pcall(function()	
		Storage:SetAsync(p.UserId, data[p.Name])
	end)
	
	if success then
		print("Success!")
	end
end

quests.Something:Connect(function() -- still not sure what this should look like
	data[p.Name]["Lumberjacking"] = 10
end)

That seems more like it, if you want the user to start off with a given amount of folders with data in it you can do this:

PlrService.onPlayerAdded:Connect(function(plr)
	if ds:GetAsync(p.UserId) then
		data[p.Name] = ds:GetAsync(p.UserId)
	else
		data[p.Name] = {["lumberjack"] = {},["Doppelsoldner"] = {},["Courtship"] = {}}
	end
end)

This will replace the usage of folders and make it neater.

1 Like

Alright, I see! Sounds good.

However, with the way my quest system works, the player doesn’t start out with a folder full of quests (instead, the quests get added to the folder throughout the game). How would I handle this approach?

basically another reason why we are using dictionaries, you can remove values from the table and assign infinite tables, now that the tables save you can do this:

local data = {["souflee"] = {["Lumberjacking"] = {}}}

table.insert(data['souflee']['Lumberjacking'],"Quest")

This will save when the user is removed

(also I gotta sleep oof)

1 Like

Thanks again so much for helping me in the right direction, have a good night! If you’re ok with it, we could surely continue this tomorrow or some other time.

Sleep tight and thanks a bunch!

Hey there, to anyone reading this. Sorry for the bump, but after a few months I found another alternative, simple although primitive solution to the problem at hand: if you make sure the quests are copied into the quest folder upon joining, you can save the values via a very simple datastore.

All you essentially have to do is check if the quest hasn’t been activated or completed yet, and you should be good to go.

Again sorry for the bump, but I just want to make sure anyone stumbling into this problem will find the solution, if I need to remove this post, I’ll do so. If you happen to want more explanation on the problem/solution, then feel free to message me. A big thanks to everyone that was kind enough to help me in the right direction! You’re all lovely folks! :smiling_face_with_three_hearts: