Hello,
I’m writing this post to get some thoughts on how you all would do the following:
I have 3 set values that I would like to save on my DataStore
Coins (IntValue)
HasItem_1(Bool Value)
HasItem_2 (Bool Value)
For these items I want them to be completely script based, in that no physical values are used. Reading up on the wiki I see that they talk about how using module scripts for your game is the way to go, but they don’t really explain it past that. So my question is how would you all go about doing this? Is using a module script the right idea? Any ideas are appreciated.
By throwing away OldValue, you’re rendering UpdateAsync no better than a SetAsync call. The advantage of the former over the latter is the script has a chance to see what the old value was immediately before computing a new value to be saved. If the saved value is changed while this call is still going, then UpdateAsync will call your function again so it can recompute a new value, which can be possibly be different based on the new OldValue.
Also, you don’t need to do this kind of object-oriented-programming to deal with DataStores. You just need a script that can keep track of a player’s data and then saves it. A table of player data tables indexed by their Player objects and a pair of PlayerAdded and PlayerRemoving event calls to load/save the data are sufficient.
Your method had no benefit over using SetAsync. You shouldn’t use bad practices knowingly when trying to help people, since that just teaches them stuff that’s wrong or pointless.
DataStores are complicated. If you don’t handle them correctly, they can accidentally overwrite values which shouldn’t be overwritten.
In multi-place games that use the same player data. I would recommend waiting until a save completes before starting the player’s teleport process, that way the other server is guaranteed to get the saved data when the player joins, rather than the old data.
You can implement a safety system to deal with data that has become outdated while in use using UpdateAsync, but how you do it would depend on the game. For example, you can use a counter like in ForeverHD’s case, or you could add the difference between the old value and the new old value to your saved data, or some other method. If you’re lazy, you can offer the player a choice between two conflicting saves, like how most cloud-based mobile apps do.
@posatta@ForbiddenJ
I think the way I implemented the UpdateAsync was really poor, but as said above, using a version system or a data comparison would justify the use. I deleted the post as it showed a prettty bad example of UpdateAsync ngl, but though its more costly to run UpdateAsync compared to SetAsync, I still believe it would be more effective in games where high traffic is involved.
Use a ModuleScript to store the values. (When the player is in game)
Save it using UpdateAsync. (@AbiZinho is right, it is better.)
DataStore:UpdateAsync(Player.UserId, function(OldData)
local Data = OldData or {} -- what we are going to save to.
for OldKey, OldValue in pairs(Data) do
for NewKey, NewValue in pairs(NewData) do -- two loops in order to check the difference between the tables.
if not Data[NewKey] then -- if new data was added
Data[NewKey] = NewValue
end
if not NewData[OldKey] then -- if the old key doesn't exist in the new table (aka if data was removed)
Data[OldKey] = nil
end
if Data[OldKey] and NewData[NewKey] then -- if there's a difference in values
if Data[OldKey] ~= NewData[NewKey] then
Data[NewKey] = NewValue
end
end
end
end
return Data
end)
DataStore keys are automatically coerced to strings in the backend. A call to tostring is unnecessary but if that’s how you want to roll, nothing lost or gained.
You should be wrapping your DataStore call in a pcall, as it is internally a web call and thus bound to error: such an example being if the endpoint is down.
Why are you looping over every value in the receiver table for each value of the old table? Imagine you have a data structure with just 8 keys in them. Your code starts a loop for each of those 8 keys from the old table, which is 8 items. You now have 8 loops and 64 iterations, which is CRAZY unnecessary.
Why are you setting data in the old table to nil?
Your UpdateAsync doesn’t return anything. UpdateAsync is a callback and requires a return value, otherwise it will not succeed.
I’ll be honest, my solution is extremely rushed. All the things you said are valid, except for the iteration thing. It’s a method I’ve been using that allows me to check the different values in two different tables. That’s what makes UpdateAsync extremely useful for me. Also, the reason why I’m setting the old data to nil is to check if data is removed. I added comments to clarify.
No, the iteration thing is very much valid. You should not be spawning x iterations every n items for a total of y iterations.
You can use the new table (considered the newest data structure for the game) as a means of retrieving key names. With these, you can also check them against the old table and see if they exist there. The same works the other way around. There is no reason why you should be nest-looping like this.
Only one loop needs to be ran and the keys of that loop can be used for both tables.
Why do you need to check that in the first place? Use the new table as the data structure and perform a transformation from the old table where necessary. The value passed in UpdateAsync should only be used as a reference and base point.
If a key exists in the old table but not in the new one, you should have little reason to bring it over. If a key exists in the new table but not the old, pass a default value or current associated data.
Templating structures really helps in these kinds of scenarios. I’ll otherwise need more information if this doesn’t answer your question, such as why you’re checking for table differences and what the relevance of that is.
Isn’t the whole reason behind UpdateAsync existing is to function as a method of comparing old values and new values? I’ve been using nested loops for a long and they function perfectly fine. I’m not sure what is wrong with that? They don’t really tank too much on preformance.
No, not really. There’s a lot more to UpdateAsync than comparing values. Value comparison is one such use case solely if you actually intend to use those to perform operations such as cancelling a write request by returning nil.
UpdateAsync mostly promotes transformation of values into a new format from an old one and (attempts to) prevent(s) race conditions in terms of writing data.
I know that the loops function fine and that performance is negligible but that’s not the point I’m trying to get across nor a concern I raised. I’m saying that they’re pointless and shouldn’t be done. Even if you do have need for multiple loops, you can achieve it in far less iterations.
I still don’t know the purpose of some elements of your code or why you believe it’s necessary to iterate over every entry of one table for every entry of another table, creating n*n iterations when you can use either n or n*2 iterations to accomplish the same thing.
In terms of n iterations, you iterate over a single table (your latest templated data structure or the one assigned to a player) and merge data from old value by the key of the running loop. If no key is found, the value is discarded, otherwise it’s brought over.
In terms of n*2 iterations, you iterate over both the new and the old table separately. Your data table should consist mostly of the new data and where necessary, incorporate keys and values from the old set.
What I’m seeing in your code is suggesting a code smell or rather just unnecessary uses of loops. If your goal is to compare, nested comparisons like this are an anti-pattern and not what you seek.