(version skip due to small patch)
Added
You can now use DataKeep with your favorite compression libraries/methods!
See new docs on the function here: Store | DataKeep
1 Like
Autosaving?!? Wait did I miss something, that sounds amazing!
It automatically saves and splits the saves up evenly between the keeps
1 Like
API Breaking Change for a better design.
:AttachSave → :PreSave & :PreLoad
Allows for more control and is not limited to just compression/decompression but transforming the data however you want.
1 Like
I just realized, the script should be a ModuleScript, shouldn’t it. This whole time I’ve put it in a ServerScript, not thinking about it.
Question, is there an easy way to get a specific player’s keep? Reason being, I want to modify some keeps using the Data Editor plugin.
Edit: Got it myself, here’s how you do it using Free Datastore Editor plugin! [21K installs] [OPEN-SOURCE]
-
Where it says Datastore, type PlayerData, and press the enter key.
-
Type Player_[UserID], and press the enter key.
-
Boom, you’re in! Edit your data as you please. ONLY EDIT THINGS UNDER THE DATA DROPDOWN, OR ELSE YOU MIGHT BREAK THINGS.
2 Likes
How do you do WriteLibs? I checked the source code, and found 0 mention of them, and when I do KeepService.WriteLib, it tells me “Cannot add property ‘WriteLib’ to table ‘Store’”.
StatManager
local Players = game:GetService("Players")
local KeepService = require(game.ReplicatedStorage.datakeep)
KeepService.WriteLib = require(game.ReplicatedStorage.WriteLib)
local DefaultData = {
Player = {
Lang = "English";
Lazercards = 0;
OwnedGuns = {"StarterPistol"};
EquippedGun = "StarterPistol";
Victories = 0;
Cosmeticards = 0;
ShirtID = 6536782130;
PantsID = 129459077;
R = .75;
G = .75;
B = .75;
OwnedShirts = {6536782130};
OwnedPants = {129459077};
Banned = false,
}
}
local KeepStore = KeepService.GetStore("PlayerData", DefaultData)
local LoadedKeeps = {}
local function onPlayerJoin(player)
KeepStore:LoadKeep("Player_" .. player.UserId):andThen(function(keep)
if keep == nil then
player:Kick("Data locked") -- will never happen, when no releaseHandler is passed it default steals from the locked session
end
keep:Reconcile()
keep:AddUserId(player.UserId) -- help with GDPR requests
keep.OnRelease:Connect(function() -- don't have to clean up, it cleans up internally.
player:Kick("Session Release")
end)
if not player:IsDescendantOf(Players) then
keep:Release()
return
end
local PlayerData = keep.Data
for i, v in pairs(DefaultData.Player) do
if PlayerData[i] == nil then
PlayerData[i] = v
end
end
print(keep)
print(PlayerData)
print(`Loaded {player.Name}'s Keep!`)
LoadedKeeps[player] = keep
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local Lazercards = Instance.new("IntValue")
Lazercards.Name = "Lazercards"
Lazercards.Parent = leaderstats
Lazercards.Value = PlayerData.Lazercards
local Cosmeticards = Instance.new("IntValue")
Cosmeticards.Name = "Cosmeticards"
Cosmeticards.Parent = leaderstats
Cosmeticards.Value = PlayerData.Cosmeticards
local Tags = Instance.new("IntValue")
Tags.Name = "Tags"
Tags.Parent = leaderstats
Tags.Value = PlayerData.Tags
local Victories = Instance.new("IntValue")
Victories.Name = "Victories"
Victories.Parent = leaderstats
Victories.Value = PlayerData.Victories
end)
end
Players.PlayerRemoving:Connect(function(player)
local keep = LoadedKeeps[player]
if keep then return end
keep:Release()
end)
KeepStore:andThen(function(store)
KeepStore = store
Players.PlayerAdded:Connect(onPlayerJoin)
end)
local DataManager = {}
function DataManager:Get(player)
local keep = LoadedKeeps[player]
if keep then
local PlayerData = keep.Data
return PlayerData
end
end
return DataManager
The WriteLib
return {
SetLazercards = function(keep, amount)
keep.Data.Lazercards = amount
end,
SetCosmeticards = function(keep, amount)
keep.Data.Cosmeticards = amount
end,
SetEquippedGun = function(keep, gun)
keep.Data.EquippedGun = gun
end,
SetOwnedGuns = function(keep, Table)
keep.Data.OwnedGuns = Table
end,
SetLang = function(keep, lang)
keep.Data.Lang = lang
end,
SetVictories = function(keep, amount)
keep.Data.Victories = amount
end,
SetBanned = function(keep, bool)
keep.Data.Banned = bool
end,
SetCloth = function(keep, clothing, item)
if clothing == "Shirt" then
keep.Data.EquippedShirt = item
elseif clothing == "Pants" then
keep.Data.EquippedPants = item
end
end,
SetSkinRGB = function(keep, color, value)
if color == "R" then
keep.Data.R = value
elseif color == "G" then
keep.Data.G = value
else
keep.Data.B = value
end
end,
SetOwnedClothes = function(keep, clothing, Table)
if clothing == "Shirt" then
keep.Data.OwnedShirts = Table
elseif clothing == "Pants" then
keep.Data.OwnedPants = Table
end
end,
}
Any idea why it won’t work?
Hey! Try:
KeepStore.Wrapper = require(game.ReplicatedStorage.WriteLib)
1 Like
Tried that, and while I didn’t get the warning, I got errors instead.
StatManager
local Players = game:GetService("Players")
local KeepService = require(game.ReplicatedStorage.datakeep)
local DefaultData = {
Player = {
Lang = "English";
Lazercards = 0;
OwnedGuns = {"StarterPistol"};
EquippedGun = "StarterPistol";
Victories = 0;
Cosmeticards = 0;
ShirtID = 6536782130;
PantsID = 129459077;
R = .75;
G = .75;
B = .75;
OwnedShirts = {6536782130};
OwnedPants = {129459077};
Banned = false,
}
}
local KeepStore = KeepService.GetStore("PlayerData", DefaultData)
KeepStore.Wrapper = require(game.ReplicatedStorage.WriteLib)
local LoadedKeeps = {}
local function onPlayerJoin(player)
KeepStore:LoadKeep("Player_" .. player.UserId):andThen(function(keep)
if keep == nil then
player:Kick("Data locked") -- will never happen, when no releaseHandler is passed it default steals from the locked session
end
keep:Reconcile()
keep:AddUserId(player.UserId) -- help with GDPR requests
keep.OnRelease:Connect(function() -- don't have to clean up, it cleans up internally.
player:Kick("Session Release")
end)
if not player:IsDescendantOf(Players) then
keep:Release()
return
end
local PlayerData = keep.Data
for i, v in pairs(DefaultData.Player) do
if PlayerData[i] == nil then
PlayerData[i] = v
end
end
print(keep)
print(PlayerData)
print(`Loaded {player.Name}'s Keep!`)
LoadedKeeps[player] = keep
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local Lazercards = Instance.new("IntValue")
Lazercards.Name = "Lazercards"
Lazercards.Parent = leaderstats
Lazercards.Value = PlayerData.Lazercards
local Cosmeticards = Instance.new("IntValue")
Cosmeticards.Name = "Cosmeticards"
Cosmeticards.Parent = leaderstats
Cosmeticards.Value = PlayerData.Cosmeticards
local Tags = Instance.new("IntValue")
Tags.Name = "Tags"
Tags.Parent = leaderstats
Tags.Value = PlayerData.Tags
local Victories = Instance.new("IntValue")
Victories.Name = "Victories"
Victories.Parent = leaderstats
Victories.Value = PlayerData.Victories
end)
end
Players.PlayerRemoving:Connect(function(player)
local keep = LoadedKeeps[player]
if keep then return end
keep:Release()
end)
KeepStore:andThen(function(store)
KeepStore = store
Players.PlayerAdded:Connect(onPlayerJoin)
end)
local DataManager = {}
function DataManager:Get(player)
local keep = LoadedKeeps[player]
if keep then
local PlayerData = keep
return PlayerData
end
end
return DataManager
GunManager
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local StatManager = require(ReplicatedStorage.StatManager)
local GunStats = require(ReplicatedStorage.GunStats)
local ReplicatedEvents = ReplicatedStorage.Events.Replicated
local BuyGun = ReplicatedEvents.BuyGun
local GetGuns = ReplicatedEvents.GetGuns
local GetGun = ReplicatedEvents.GetGun
local SetGun = ReplicatedEvents.SetGun
local Guns = ReplicatedStorage:WaitForChild("Guns")
BuyGun.OnServerInvoke = function(Player, Gun)
local keep = StatManager:Get(Player)
print(keep.Data)
if keep.Data then
local MockTable = keep.Data.OwnedGuns
if keep.Data.Lazercards >= GunStats.Guns[Gun]["Price"] then
keep:SetLazercards(keep.Data.Lazercards - GunStats.Guns[Gun]["Price"])
table.insert(MockTable, Gun)
keep:SetOwnedGuns(MockTable)
return true
else
return false
end
else
return false
end
end
SetGun.OnServerInvoke = function(Player, Gun)
local keep = StatManager:Get(Player)
--print(keep.Data)
if keep.Data then
if table.find(keep.Data.OwnedGuns, Gun) then
keep:SetEquippedGun(Gun)
return true
else
return false
end
else
return false
end
end
GetGuns.OnServerInvoke = function(Player, Type)
local keep = StatManager:Get(Player)
print(keep.Data.OwnedGuns)
print(Type)
local GunsToReturn = {}
pcall(function()
if keep.Data then
if Type == "Owned" then
GunsToReturn = keep.Data.OwnedGuns
elseif Type == "Store" then
print("Getting store guns")
for i, v in pairs(Guns:GetChildren()) do
print("Checking "..v.Name)
if not table.find(keep.Data.OwnedGuns, v.Name) then
print(v.Name)
table.insert(GunsToReturn, v.Name)
end
end
end
end
end)
if #GunsToReturn > 0 then
return GunsToReturn
else
return false
end
end
GetGun.OnServerInvoke = function(Player)
local keep = StatManager:Get(Player)
print(keep.Data.EquippedGun)
local Item
pcall(function()
if keep.Data then
Item = Guns:WaitForChild(keep.Data.EquippedGun)
end
end)
if Item ~= nil then
return Item.Name
else
return false
end
end
So sorry, it is not keepStore.Wrapper, but DataKeep.Wrapper
Should this be moved under keepstore to allow each keepstore a custom writelib?
1 Like
Thanks!
Maybe, that could be a cool idea! Maybe you should do a poll, see how people in general feel about it
That works, thank you!
I am getting this warning, but I wont worry about it for now.
Type Error: (27,23) Table type '{| SetBanned: (any, any) -> (...any), SetCloth: (any, any, any) -> (...any), ... 8 more ... |}' not compatible with type '{| Mutate: (any, string, any) -> (...any), onDataChange: (any, string, any) -> (...any) |}' because the former is missing fields 'Mutate', and 'onDataChange'
What module is this from? This is just a typing warning
1 Like
Sorry for the late response, it’s from the StatManager Module.
I’m wondering if that warning is related to an issue that has popped up, where data isn’t saved anymore. I’ll use the WriteLib functions, and Data.EquippedGun get’s changed to the correct value (same with all other WriteLib functions), but then the correct value isn’t saved to the datastore. It get’s reset to the previous value, not the default.
StatManager
local Players = game:GetService("Players")
local KeepService = require(game.ReplicatedStorage.datakeep)
local DefaultData = {
Player = {
Lang = "English";
Lazercards = 0;
OwnedGuns = {"StarterPistol"};
EquippedGun = "StarterPistol";
Victories = 0;
Cosmeticards = 0;
ShirtID = 6536782130;
PantsID = 129459077;
R = .75;
G = .75;
B = .75;
OwnedShirts = {6536782130};
OwnedPants = {129459077};
Banned = false,
}
}
local KeepStore = KeepService.GetStore("PlayerData", DefaultData)
KeepService.Wrapper = require(game.ReplicatedStorage.WriteLib) -- The code giving a typing warning.
local LoadedKeeps = {}
local function onPlayerJoin(player)
KeepStore:LoadKeep("Player_" .. player.UserId):andThen(function(keep)
if keep == nil then
player:Kick("Data locked") -- will never happen, when no releaseHandler is passed it default steals from the locked session
end
keep:Reconcile()
keep:AddUserId(player.UserId) -- help with GDPR requests
keep.OnRelease:Connect(function() -- don't have to clean up, it cleans up internally.
player:Kick("Session Release")
end)
if not player:IsDescendantOf(Players) then
keep:Release()
return
end
local PlayerData = keep.Data
for i, v in pairs(DefaultData.Player) do
if PlayerData[i] == nil then
PlayerData[i] = v
end
end
print(keep)
print(PlayerData)
print(`Loaded {player.Name}'s Keep!`)
LoadedKeeps[player] = keep
local leaderstats = Instance.new("Folder")
leaderstats.Name = "leaderstats"
leaderstats.Parent = player
local Lazercards = Instance.new("IntValue")
Lazercards.Name = "Lazercards"
Lazercards.Parent = leaderstats
Lazercards.Value = PlayerData.Lazercards
local Cosmeticards = Instance.new("IntValue")
Cosmeticards.Name = "Cosmeticards"
Cosmeticards.Parent = leaderstats
Cosmeticards.Value = PlayerData.Cosmeticards
local Tags = Instance.new("IntValue")
Tags.Name = "Tags"
Tags.Parent = leaderstats
Tags.Value = PlayerData.Tags
local Victories = Instance.new("IntValue")
Victories.Name = "Victories"
Victories.Parent = leaderstats
Victories.Value = PlayerData.Victories
end)
end
Players.PlayerRemoving:Connect(function(player)
local keep = LoadedKeeps[player]
if keep then return end
keep:Release()
end)
KeepStore:andThen(function(store)
KeepStore = store
Players.PlayerAdded:Connect(onPlayerJoin)
end)
local DataManager = {}
function DataManager:Get(player)
local keep = LoadedKeeps[player]
if keep then
local PlayerData = keep
return PlayerData
end
end
return DataManager
WriteLib is the exact same as before.
GunManager
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local StatManager = require(ReplicatedStorage.StatManager)
local GunStats = require(ReplicatedStorage.GunStats)
local ReplicatedEvents = ReplicatedStorage.Events.Replicated
local BuyGun = ReplicatedEvents.BuyGun
local GetGuns = ReplicatedEvents.GetGuns
local GetGun = ReplicatedEvents.GetGun
local SetGun = ReplicatedEvents.SetGun
local Guns = ReplicatedStorage:WaitForChild("Guns")
BuyGun.OnServerInvoke = function(Player, Gun)
local keep = StatManager:Get(Player)
print(keep.Data)
if keep.Data then
local MockTable = keep.Data.OwnedGuns
if keep.Data.Lazercards >= GunStats.Guns[Gun]["Price"] then
keep:SetLazercards(keep.Data.Lazercards - GunStats.Guns[Gun]["Price"])
table.insert(MockTable, Gun)
keep:SetOwnedGuns(MockTable)
return true
else
return false
end
else
return false
end
end
SetGun.OnServerInvoke = function(Player, Gun)
local keep = StatManager:Get(Player)
--print(keep.Data)
if keep.Data then
if table.find(keep.Data.OwnedGuns, Gun) then
print(keep.Data.EquippedGun)
keep:SetEquippedGun(Gun)
print(keep.Data.EquippedGun)
return true
else
return false
end
else
return false
end
end
GetGuns.OnServerInvoke = function(Player, Type)
local keep = StatManager:Get(Player)
print(keep.Data.OwnedGuns)
print(Type)
local GunsToReturn = {}
pcall(function()
if keep.Data then
if Type == "Owned" then
GunsToReturn = keep.Data.OwnedGuns
elseif Type == "Store" then
print("Getting store guns")
for i, v in pairs(Guns:GetChildren()) do
print("Checking "..v.Name)
if not table.find(keep.Data.OwnedGuns, v.Name) then
print(v.Name)
table.insert(GunsToReturn, v.Name)
end
end
end
end
end)
if #GunsToReturn > 0 then
return GunsToReturn
else
return false
end
end
GetGun.OnServerInvoke = function(Player)
local keep = StatManager:Get(Player)
print(keep.Data.EquippedGun)
local Item
pcall(function()
if keep.Data then
Item = Guns:WaitForChild(keep.Data.EquippedGun)
end
end)
if Item ~= nil then
return Item.Name
else
return false
end
end
(accidentally skipped 2.1.0)
API Breaking Change
Fixed
- ViewKeep :Save not working, replaced with :Overwrite()
- #12
Added
1 Like
Updated to version 2.2.1 (as listed on GitHub), still having the same issue. I also tested in a live client, along with studio, and still had the same issue. I’m getting no errors, which is confusing me more. I don’t know what in specific I’m doing wrong. Sorry for asking for help so much btw
BTW, the link for 2.1.1 is leading to a 404 for me, so I’ll link the latest version:
Version 2.2.1 GitHub
Is anything coming through the error signal?
1 Like
Error signal? What’s that? I checked my output, there were no errors, checked the dashboard error report for the live test, and there was also nothing.
My theory is that it’s a result of changing the wrapper to be my writelib, but if that’s the case, I don’t know how to change data values. Changing them without the writelib also didn’t work. I have the settings set so that studio let api requests work.
Sorry, seems I missed that in documentation. Try
KeepStore.IssueSignal:Connect(function(err)
warn(err)
end)
added that to documentation
1 Like