I’m currently working on a simple mining game just for fun. It’s inherently based on a kit someone else made and I’ve been able to work with it for a long time, however there’s been a glaring issue with datastores since the beginning that I can’t find the root cause of and forces me to make my game solo just for new players.
Problem
The problem seems to be that when a completely new player (so, probably without a datastore) joins another player, their inventory becomes shared with the other until the new player leaves and rejoins. This can cause two or more people to end up with the same inventory.
I have tried looking into the situation myself on numerous occasions as well as searching up this issue, but I still can’t exactly figure out the problem. I know there’s two major scripts that handle the early stages of datastore functionality which I will send shortly. I’m hoping someone can figure out where the source of the issue is stemming from, so I’ll send the entire scripts below. Sorry if it’s too wall-of-text-y.
Note: I want to emphasize that the problem seems to go away when the players rejoin. Multiplayer works fine assuming all players already have joined a solo player beforehand and mined a few things. Unfortunately this also doesn’t seem to show any related errors when it occurs. This game does send errors but it’s mostly just cosmetic fluff…
The “Data” script found in ServerScriptService. Seems to handle the initialization and core of the datastores.
-- Data script | oofblocks 5/25/21
local replicatedStorage = game:GetService("ReplicatedStorage")
local serverStorage = game:GetService("ServerStorage")
local dataStoreService = game:GetService("DataStoreService")
local players = game:GetService("Players")
local store = dataStoreService:GetDataStore("UniverseTestingData")
local config = require(workspace.Configuration)
local cache = {}
local dataTemplate = {
Inventory = {};
Pickaxes = {};
Pickaxe = "Default Pickaxe";
OreMined = 0;
Level = 1
}
for _, data in pairs(config.Alts) do
dataTemplate[data.Name .. "Inventory"] = {}
end
local function getData(plr)
if cache[plr.Name] then
return cache[plr.Name]
end
local success, data = pcall(function()
return store:GetAsync(plr.UserId)
end)
if success then
cache[plr.Name] = data or dataTemplate
return cache[plr.Name]
end
cache[plr.Name] = dataTemplate
return cache[plr.Name]
end
replicatedStorage.GetData.OnServerInvoke = getData
serverStorage.GetDataAgain.OnInvoke = getData
local function added(plr)
local data = getData(plr)
for key, value in pairs(dataTemplate) do
if not data[key] then
data[key] = value
end
end
for _, ore in pairs(replicatedStorage.Ores:GetChildren()) do
repeat wait() until ore
for k, v in pairs(data) do
if string.sub(k,-9) == "Inventory" then
v[ore.Name] = v[ore.Name] or 0
end
end
end
for _, pickaxe in pairs(replicatedStorage.Pickaxes:GetChildren()) do
if pickaxe.Name ~= "Default Pickaxe" then
data.Pickaxes[pickaxe.Name] = data.Pickaxes[pickaxe.Name] or false
else
data.Pickaxes["Default Pickaxe"] = true
end
end
local function createVal(class, name, val)
local v = Instance.new(class)
v.Name = name
v.Value = val
v.Parent = plr
return v
end
createVal("IntValue", "Level", data.Level)
createVal("IntValue", "OreMined", data.OreMined)
createVal("StringValue", "Pickaxe", data.Pickaxe)
cache[plr.Name] = data
end
players.PlayerAdded:Connect(added)
local function save(plr)
local data = getData(plr)
data.Level = plr.Level.Value
data.OreMined = plr.OreMined.Value
data.Pickaxe = plr.Pickaxe.Value
local success, err = pcall(function()
store:SetAsync(plr.UserId, data)
end)
if not success then
warn(string.format("Error saving data for %s: %s", plr.UserId, err))
else
print(string.format("Successfully saved %s's data", plr.UserId))
end
end
players.PlayerRemoving:Connect(save)
local function saveAll()
for _, plr in pairs(players:GetPlayers()) do
save(plr)
end
end
game:BindToClose(saveAll)
coroutine.wrap(function()
while wait(120) do
saveAll()
end
end)()
local function awardOre(plr, ore, amount, alt)
local data = getData(plr)
local tab = data["Inventory"]
if alt then
if alt == 1 then
tab = data["FunkyInventory"]
elseif alt == 2 then
tab = data["SinisterInventory"]
elseif alt == 3 then
tab = data["MutilatedInventory"]
else
tab = data[alt .. "Inventory"]
end
if not tab then
return warn(string.format("Altered type '%s' does not exist!", alt))
end
end
if tab[ore] then
tab[ore] += amount
replicatedStorage.UpdateInventory:FireClient(plr, data)
return
end
return warn(string.format("Ore %s does not exist!", ore))
end
serverStorage.AwardOre.OnInvoke = awardOre
local replicatedStorage = game:GetService("ReplicatedStorage")
local serverStorage = game:GetService("ServerStorage")
local function haveit(plr,Item)
local data = getData(plr)
if data.Inventory[Item] > 0 or
data.FunkyInventory[Item] > 0 or
data.SinisterInventory[Item] > 0 or
data.MutilatedInventory[Item] > 0
then
return true
else
return false
end
end
serverStorage.Haveit.OnInvoke = haveit
local function craftPickaxe(plr, pickaxe)
local data = getData(plr)
if data then
local realPickaxe = replicatedStorage.Pickaxes:FindFirstChild(pickaxe)
local items = realPickaxe.Cost:GetChildren()
local score = 0
for _, v in pairs(items) do
if
data.Inventory[v.Name] >= v.Value or
data.FunkyInventory[v.Name] >= v.Value or
data.SinisterInventory[v.Name] >= v.Value or
data.MutilatedInventory[v.Name] >= v.Value
then
score += 1
end
end
if score == #items then
plr.Pickaxe.Value = pickaxe
data.Pickaxes[pickaxe] = true
return true
end
return false
end
return false
end
replicatedStorage.CraftPickaxe.OnServerInvoke = craftPickaxe
local function equipPickaxe(plr, pickaxe)
local data = getData(plr)
if data.Pickaxes[pickaxe] then
plr.Pickaxe.Value = pickaxe
end
end
replicatedStorage.EquipPickaxe.OnServerEvent:Connect(equipPickaxe)
The start of the Main file, in the same location. Just in-case this is causing the issue too. I tried commenting out the data lines here but it seems like an important failsafe.
-- Main handler for the game | oofblocks 5/24/21
local replicatedStorage = game:GetService("ReplicatedStorage")
local serverStorage = game:GetService("ServerStorage")
local http = game:GetService("HttpService")
local players = game:GetService("Players")
local config = require(workspace.Configuration)
local altify = require(replicatedStorage.Altify)
-- important
workspace.Regen.Position = Vector3.new(math.floor(workspace.Regen.Position.X),math.floor(workspace.Regen.Position.Y),math.floor(workspace.Regen.Position.Z))
local regenPos, regenCF = workspace.Regen.Position, workspace.Regen.CFrame
for _, v in pairs(workspace.Plugins:GetChildren()) do
if v:IsA("ModuleScript") then
require(v)()
end
end
local function added(p)
if table.find(config.Banned, p.Name) or table.find(config.Banned, p.UserId) then
p:Kick("banned nerd")
end
local data = serverStorage.GetDataAgain:Invoke(p)
replicatedStorage.UpdateInventory:FireClient(p, data)
if config.OrePlacer then
local placer = serverStorage["ore placer"]:Clone()
placer.Parent = p:WaitForChild("Backpack")
end
end
players.PlayerAdded:Connect(added)