Attempt to call a table value Profile Service

Im using profile service for my inventory saving system, I have an EquippedWeapons key which stores the player’s equipped weapon. When the player joins, I have a server script that get the Equipped weapons but I’m getting “Attempt to call a table value” error

-- part of the code that is revelant, is inside a player added function
local equipped = PlayerDataHandler:getEquippedWeapons(plr) -- this function is erroring
		char:SetAttribute("CurrentWeapon", equipped[1])

		plr.CharacterAppearanceLoaded:Connect(function(char)
			local weaponName = char:GetAttribute("CurrentWeapon")
			local Weapon = WeaponModels[weaponName]:Clone()

This is the playerdatahandler module here is some of the relevant functions

function PlayerDataHandler:getEquippedWeapons(plr)
	local equipped = PlayerDataHandler:Get(plr, "EquippedWeapons") -- Get called here
	return equipped
end

local function getProfile(plr)
	local profile = Profiles[plr]
	if not profile then
		profile = { Data = dataTemplate() } -- attempt to call a table value error here
		Profiles[plr] = profile
	end
	return Profiles[plr]
end

function PlayerDataHandler:Get(plr, key)
	local profile = getProfile(plr) -- get profile called here
	assert(profile.Data[key], string.format("data does not exist for key: %s", key))

	return profile.Data[key]
end

bump
30char30char30char30char30char

Can you clarify which line is throwing the error? I’d guess it’s “local Weapon = WeaponModels[weaponName]:Clone()”

the error is Attempt to call a table value here

1 Like

Is the function datatable() actually returning a table?

are you referring to dataTemplate()? Sorry I only understand Profile Service on a very surface level, i know how to make the functions for changing my different datas but I dont really understand how it saves data since I made it while watching a tutorial

Woops yeah that’s what I meant

Im sorry im not sure how I should find out if its returning a table.

Could you show me the code inside of dataTemplate() please?

dataTemplate is a dictionary of all of the data I want to save (exp, cash, items, etc)

local dataTemplate = {
	Cash = 0,
	Exp = 0,
	level = 0,
	Inventory = {},
	EquippedWeapons = {},
	EquippedSpells = {},
	EquippedArmour = {},
	EquippedRelics = {},
	Spells = {},
	settings = {},
	spawnLocation = ""
}

and then it does somethign with profile service

local ProfileStore = ProfileService.GetProfileStore(
	"PlayerProfile",
	dataTemplate
)

Well the issue here is that dataTemplate is a dictionary, not a function.
In your line profile = {Data = dataTemplate()}, those parenthesis mean the code is trying to run a function declared dataTemplate and assigned the returned value to Data, not the dictionary itself like you’ve shown.

It was working up until I was trying to find a work around to use the :Get function inside of a script outside of PlayerDataHandler. If this is the case then how should I find a work around that wont interfere with whatever GetProfileStore does?

I guess I should just change the topic since all I want to do is get the “EquippedWeapons” data from PlayerDataHandler.

I’m not exactly sure what you mean, but overall I think your logic approaching this is flawed.

local ProfileStore = ProfileService.GetProfileStore(
	"PlayerProfile",
	dataTemplate
)

Should only be declared once somewhere at the top of your module.
When the player joins, hook a function via Players.PlayerAdded
The function will then attempt to load the player’s profile by doing:

Players.PlayerAdded:Connect(function(player)

     local profile = ProfileStore:LoadProfileAsync(
          "your_profile_name_here",
          "ForceLoad"
     )
      --> "your_profile_name_here" defines exactly what saved data we want, and it is a mustthat
       -- that you include the player's UserId for a unique key. ex: "Player_Data_"..player.UserId
       -- "ForceLoad" is a means of retrieving the profile, and I advise you read ProfileService's API
       -- documentation for further information

     if not profile then
          player:Kick("Could not retrieve profile. Please rejoin.")
          return
     else
          PlayerDataHandler.Profiles[player.UserId] = profile
          --> do other logic with your loaded profile here, such as 
           -- hooking a profile:ListenToRelease() function
     end

end)

function PlayerDataHandler.GetProfile(player)
     return PlayerDataHandler.Profiles[player.UserId]
end

-- accessing a profile from another server script
local profile = PlayerDataHandler:GetProfile(player)
local playerData = profile.Data

Essentially what we’re doing is attempting to load the player’s profile, via ProfileStore:LoadProfileAsync(), and if it does not return a profile (data stores may be down) then simply kick the player from the game, since we do not want to let the player in the game if there are active issues with DataStoreService.

Do note that when indexing the player’s retrieved profile in PlayerDataHandler.Profiles, do not assign profile.Data, but the profile itself because we want to be able to release the profile when the player leaves the game (profile:Release()), and this cannot be done if the player’s profile data is returned from PlayerDataHandler.GetProfile() instead of the actual profile itself.

Here is the official ProfileService API for more information: API

Yes this is all done I didnt include everything in my script because it would be too long but now that you mention my flawed logic i have no idea why I though I couldnt call Get from a script but thank you for your time dealing with my bad case of tunnel vision

I was able to use simply use “:Get” to get the data the problem probably lies with this function that I didnt put much though into

Instead of having a whole separate function to get the player’s equipped weapon, you could just do

local profile = PlayerDataHandler.GetProfile(plr)
local equippedWeapons = profile.Data.EquippedWeapons

Or if you must,

function PlayerDataHandler.GetKeyValue(plr, key)
     local profile = PlayerDataHandler.GetProfile(plr)
     return profile.Data[key]
end

-- accessing equipped weapons from another script
local equippedWeapons = PlayerDataHandler.GetKeyValue(plr, "EquippedWeapons")

Ultimately your PlayerDataHandler.GetProfile() (defined as :Get() by you) function should be no longer than one line which returns the loaded profile that was previously indexed to PlayerDataHandler.Profiles in the PlayerAdded function. The code below in your current function is unnecessary.

if not profile then
	profile = {Data = dataTemplate}
	Profiles[plr] = profile
end

yeah im sorry I just realized there was alot of things I didnt mention which probably caused you alot of confusion
this was my :Get function I was mentioning earlier

function PlayerDataHandler:Get(plr, key)
	local profile = getProfile(plr)
	assert(profile.Data[key], string.format("data does not exist for key: %s", key))

	return profile.Data[key]
end

local function getProfile(plr)
	assert(Profiles[plr], string.format("Profile does not exist for %s", plr.UserId))
	local profile = Profiles[plr]
	if not profile then
		profile = { Data = dataTemplate() }
		print(profile)
		Profiles[plr] = profile
	end
	return Profiles[plr]
end

I also appreciate that your explaining me what each function does because I really need to learn it so I wont have as much trouble next time :+1:

1 Like

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