- What do you want to achieve? Keep it simple and clear!
I want to create a leveling system with xp, an xp multiplier, rebirths, etc. I am additionally trying to add currency such as coins and diamonds and an inventory system for cosmetics and boost items. I want to use ProfileStore and Replica to achieve this. XP is given when a player collects an item on the map. I have created a script to handle item collection easier instead of each item having scripts but it does not work properly (see below).
- What is the issue? Include screenshots / videos if possible!
I am unable to write to a player’s profile from any script other than the one where the profile is created. I get the following error when the player attempts to collect an item:
14:52:13.704 ServerScriptService.CollectableManager:17: attempt to index nil with Instance - Server - CollectableManager:17
- What solutions have you tried so far? Did you look for solutions on the Creator Hub?
I have tried several solutions, most of which failed. I put the scripts through several AI chatbots which were unable to solve the problem.
Scripts:
ProfileManager (Server Script)
local ProfileStore = require(game.ServerScriptService.ProfileStore)
local LevelModule = require(game.ServerScriptService.LevelModule)
local PROFILE_TEMPLATE = {
-- Progression
Level = 1,
XP = 0,
XPMultiplier = 1,
Rebirths = 0,
-- Currency
Coins = 0,
Diamonds = 0,
-- Inventory
Inventory = {
-- Skins
Skins = {
["Default"] = {Owned = true, Equipped = true},
},
-- Items
Items = {
},
},
}
local Players = game:GetService("Players")
local PlayerStore = ProfileStore.New("PlayerStore", PROFILE_TEMPLATE)
local Profiles: {[Player]: typeof(PlayerStore:StartSessionAsync())} = {}
local function PlayerAdded(player)
local profile = PlayerStore:StartSessionAsync(`{player.UserId}`, {
Cancel = function()
return player.Parent ~= Players
end,
})
if profile ~= nil then
profile:AddUserId(player.UserId)
profile:Reconcile()
profile.OnSessionEnd:Connect(function()
Profiles[player] = nil
player:Kick(`Profile session end - Please rejoin`)
end)
if player.Parent == Players then
Profiles[player] = profile
print(`Profile loaded for {player.DisplayName}!`)
LevelModule:AddXP(profile.Data, 0)
else
profile:EndSession()
end
else
player:Kick(`Profile load fail - Please rejoin`)
end
end
for _, player in Players:GetPlayers() do
task.spawn(PlayerAdded, player)
end
Players.PlayerAdded:Connect(PlayerAdded)
Players.PlayerRemoving:Connect(function(player)
local profile = Profiles[player]
if profile ~= nil then
profile:EndSession()
end
end)
local function GrantXP(player, amount)
local profile = Profiles[player]
if profile then
LevelModule:AddXP(profile.Data, amount)
end
end
LevelModule (Module Script)
local LevelModule = {}
local XP_CONSTANT = 100
local XP_SCALE = 20
local LEVEL_CAP = 20
function LevelModule:XPForNextLevel(level)
return XP_CONSTANT + (level * XP_SCALE)
end
function LevelModule:AddXP(profile, amount)
if not profile then return end
local xpToAdd = amount * profile.XPMultiplier
profile.XP = profile.XP + xpToAdd
while profile.Level < LEVEL_CAP and profile.XP >= self:XPForNextLevel(profile.Level) do
profile.XP = profile.XP - self:XPForNextLevel(profile.Level)
profile.Level = profile.Level + 1
print("Level up! New level:", profile.Level)
end
end
return LevelModule
CollectableManager (Server Script)
local Players = game:GetService("Players")
local CollectionService = game:GetService("CollectionService")
local ServerStorage = game:GetService("ServerStorage")
local ProfileStore = require(game.ServerScriptService.ProfileStore)
local LevelModule = require(game.ServerScriptService.LevelModule)
local ItemsStorage = ServerStorage:WaitForChild("ItemsStorage")
local function handleTouch(part)
local character = part.Parent
if not character then return end
local player = Players:GetPlayerFromCharacter(character)
if not player then return end
local profile = Profiles[player]
if not profile then return end
local item = part
local itemData = item:FindFirstChild("Data")
if not itemData then return end
local minLevel = itemData:FindFirstChild("Level") and itemData.Level.Value or 1
local xpValue = itemData:FindFirstChild("Xp") and itemData.Xp.Value or 0
local coinValue = itemData:FindFirstChild("Coins") and itemData.Coins.Value or 0
local cooldown = itemData:FindFirstChild("Cooldown") and itemData.Cooldown.Value or 0
if profile.Data.Level < minLevel then return end
if xpValue > 0 then
LevelModule:AddXP(profile.Data, xpValue)
end
if coinValue > 0 then
profile.Data.Coins += coinValue
end
item.Parent = ItemsStorage
task.delay(cooldown, function()
item.Parent = workspace
end)
end
for _, item in ipairs(CollectionService:GetTagged("Collectable")) do
if item:IsA("BasePart") then
item.Touched:Connect(handleTouch)
end
end
CollectionService:GetInstanceAddedSignal("Collectable"):Connect(function(item)
if item:IsA("BasePart") then
item.Touched:Connect(handleTouch)
end
end)
Apologies for the code being unorganized. I am prototyping a project to see if it will work or not.
Attached is the place file with all of the assets removed except for the ones that I am trying to debug.
UntitledPlace.rbxl (105.1 KB)
