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'
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
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:
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.
I’m getting errors again, which is a VERY good thing! VERY good update.
You should probably change the WriteLib documentation, as it still says DataKeep.WriteLib = require(path_to_WriteLib). That might confuse people a lot.
Sorry to bother you, just wanted to let you know that the RBXM file wasn’t included in that release. I’ll just copy the code in the changelog to my copy of 3.0.0 to fix it
Done some testing, and it’s weird. It can create a new keep and save that just fine, and it can load a keep just fine, but saving later changes to the keep doesn’t seem to work. LoadCount gets set to 2 ingame, but stays as 1 in the datastore.
I can test that with the Data Editor plugin, using it to delete my pre-existing keep, and changing some data values in the keep.
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.Releasing:Connect(function() -- don't have to clean up, it cleans up internally.
player:Kick("Session Release")
end)
keep.Saving:Connect(function()
print("Saving keep")
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
KeepStore.IssueSignal:Connect(function(err)
warn(err)
end)
KeepStore.CriticalStateSignal:Connect(function()
warn("Critical State!")
end)
return DataManager
All though this is nicely done and better than using profile service, I would recommend; that if you have not created your own cached session locking based datastore you should do that. It will help you learn more about the workings of the API when you come across issues.
How to create a session locker efficiently:
Use updateasync so you can get data and set data at the same time. You can set a boolean in the database to see if there is an existing session. If there is an active session retry every second. If after 20 seconds the session is still locked the server might have crashed and just load their most recent data. It is also important to use promises and listen for errors to retry.
Issue was fixed by changing the SaveInterval in src to 5. I don’t know if there’s a way to have it save data when releasing the keep, but saving on an interval of 5 will make saving more consistent.