How would I go about creating a leveling system?

  1. 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).

  1. 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
  1. 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)

I see issue with handle touch, your code assumes a body part might have Data inside of it, since part is the part that actually touched and it’s parent is Model

Also are you actually returning Profiles dictionary?

Edit:


I was right, you didn’t even declare

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.