Don't know where to store inventory stuff

Generally a bad idea to allow the client to access data directly as it can be easily exploited. ServerStorage is the recommended route as only the server has access to it. You will need to setup server and client sided scripts using RemoteFunctions & RemoteEvents to get data.

5 Likes

So prob my best option is to make that everytime i need to access the inventory i do something like:

local inventory = replicatedstorage:FireServer()

and the script

replicatedstorage.OnServerEvent:Connect(function()
        --stuff
        return stuff
end)

?

2 Likes

Depending on how often the inventory would be updated, another approach that you could consider is firing a client event every time an item was added or removed.

To do this, I would store all of your inventory items in a table, use a proxy table, and use the __newindex metamethod to track each change. This would increase the user experience as each change is updated in real time.

However, I would only consider doing this if you think the server can handle it (ie: player count supports it, wouldn’t be fired too many times)

2 Likes

what do you mean with the __NewIndex method?

2 Likes

any example code or the documentation page?

2 Likes
local containerTable = {}

local cnTable = {}
local function checkPlayerStatus(self, key, value)
	if value ~= nil then
		local connection = value.Character.Humanoid.Animator.AnimationPlayed:Connect(function(anim)
			checkPlayersAnimations(value, anim)
		end)
		containerTable[key] = value
		cnTable[key] = connection
	else
		if cnTable[key] then
			cnTable[key]:Disconnect()
			cnTable[key] = nil
			containerTable[key] = nil
		end
	end
end

local proxy = setmetatable({}, {__newindex = function(self, key, value) checkPlayerStatus(self, key, value) end})

local function listenToAnimations(player)
	local char = player.Character or player.CharacterAdded:Wait()
	player.playerstats.InGame.Changed:Connect(function(val)
		if val then
			proxy[player] = player
		else
			proxy[player] = nil
		end
	end)
end

Alright so this is the most recent use I’ve had with recent newindex metamethod, and basically it fires every time something is added to the table. In order to use metamethods, you have to use setmetatable. And yes there is code on the docs.

The reason you would want to use proxy’s in this case is because __newindex only fires when the thing you tried to set was equal to nil, so by using a proxy, you would always be indexing nil, therefore always firing __newindex.

Another is note is that metamethods like __newindex are basically just events tailored for metatables if that helps.

https://create.roblox.com/docs/luau/metatables
I would look into this if you want to create systems such as this. If you want to learn about metatables in general than you can use these tutorials:
https://devforum.roblox.com/t/full-beginner-guide-to-metatables-what-are-they-and-every-metamethod-explained/2505946
https://devforum.roblox.com/t/all-you-need-to-know-about-metatables-and-metamethods/503259

Sorry if this post came out a bit rushed.

2 Likes

If that wasn’t helpful enough then I will try to explain how this would be useful in your case.
Instead of only firing a server event when you wanted to access the player’s inventory, you can just fire every time there was a change to the user’s inventory on a client event.

-- Lets pretend that this is the user's inventory in a table and that we have all of them for each player

local inventory = {}

But how will we be able to track when the inventory is changed? We can use the newindex metamethod for that.

-- Lets pretend that this is the user's inventory in a table and that we have all of them for each player

local clientRemoteEvent = game.ReplicatedStorage.RemoteEvent -- Lets pretend that this is the event that we want to use

local function sendInventoryToClient(self, key, value)
	clientRemoteEvent:FireClient(self)
end

local inventory = setmetatable({}, {__newindex = function(self, key, value) sendInventoryToClient(self, key, value) end})

The newindex metamethod automatically passes three parameters, the table that it has happened to, the location, and the value it was set to. (if you are wondering about self, I just named it that to make it more clear that we are referring to the table as self).

After that all we are doing is sending self (the inventory table) to the client and boom. That is pretty much it, now whenever there is a change to the inventory table, an event sending the table to the client will automatically be fired.

Now if this is all you need then you could end there, but you may have run into a slight problem where newindex doesn’t fire when the original value was already set.

So how do we fix this problem? We use a proxy table.
The only role of the proxy is just to make sure that the values are always equal to nil so that the newindex metamethod always fires.

-- Lets pretend that this is the user's inventory in a table and that we have all of them for each player

local clientRemoteEvent = game.ReplicatedStorage.RemoteEvent -- Lets pretend that this is the event that we want to use

local inventory = {}

local function sendInventoryToClient(self, key, value)
	inventory[key] = value
	clientRemoteEvent:FireClient(inventory)
end

local proxy = setmetatable({}, {__newindex = function(self, key, value) sendInventoryToClient(self, key, value) end})

Since we are using a proxy, we have to manually set the value in the inventory, and since self now equals the proxy table, we have to directly send the inventory table over to the client. And then, you’re done!

I hope this explains it better.

3 Likes

in my case the “inventory” items were set on a module script, so how can i access them? and im still a bit confused on how many stuff does that code support, cause each “Mod” has like 17 different values

1 Like

There is no limit to how much can be stored as metatables ,as they are just normal tables with metamethods attached. The only thing I would be worried about is how often the remote events would be fired. As long as there aren’t over like 50 players in a server, this should work fine.

And for all the values in the table, as long as you aren’t storing a table inside of a table, the newindex metamethod will do all the work for everything and there wouldn’t be any problems.
If you are worried about it from a performance standpoint then I don’t see anything to be worried about other than firing dozens of events per second if there are so many changes.

Oh yeah, and I forgot to answer your original table about how you could access the inventory from a module, all you need to do is require the module.

-------------------------------------------------------------- Module
local inventory = {}



-------------------------------------------------------------- Server Script

local InventoryModule = require(game.ServerScriptService.InventoryModule) -- Wherever you stored the module

local inventory = InventoryModule.inventory
local function sendInventoryToClient(self, key, value)
	inventory[key] = value
end


local proxy = setmetatable({}, {__newindex = function(self, key, value) sendInventoryToClient(self, key, value) end})
1 Like

So basically the only changes it should receive are pretty small for example a change of when you add them and a change of when you remove them, plus the server size would be up to 16 so i don’t think the problem of it breaking, but i don’t actually understand how do i use stuff inside that, rn im looking at the tutorials you sent me

edit: all i understood so far is that metatables are like Events but tables, tbh i still don’t know how to make them work tho

Alright so it’s actually very simple.
Let’s say a player gained a gold helmet and you wanted to add it to his inventory.

All you have to do is change the proxy:

-------------------------------------------------------------- Module
local inventory = {}



-------------------------------------------------------------- Server Script

local InventoryModule = require(game.ServerScriptService.InventoryModule) -- Wherever you stored the module

local inventory = InventoryModule.inventory
local function sendInventoryToClient(self, key, value)
	inventory[key] = value
end


local proxy = setmetatable({}, {__newindex = function(self, key, value) sendInventoryToClient(self, key, value) end})


-------------------------------------------------------------- Inventory Changes

proxy.GoldHelmet = {color = "Gold", helmetType = "Player Helmet", rarity = "Legendary"}

Because we already automatically set the values from the proxy into the inventory table, the gold helmet will automatically be added.

print(inventory) -- Will now have GoldHelmet inside of it

And we need no further changes every time we want to add something to the inventory.

Gonna try it, but im having issues on adding __newindex since i don’t know how to find it
image

That’s perfectly fine. You just have to set the newindex metamethod equal to something. In this case, I set it to a function so that every time the newindex metamethod happens, the function plays.

function DoThis()
	
end

proxy = setmetatable({}, {__newindex = DoThis()})

ok, tried doing that but im having some issues on cause: Attempted to index nil with “GoldHelmet”

does goldhelmet should appear inside the module too?

Can I see the code you are working with so I can better try to solve them?

Nvm i managed to fix it, i needed to do this
image
to make it properly work

but now the question kinda rises, how i can access something that has something like this?

like how do i make it that the preset is inside the module script, but i want it to set to the “inventory” table inside the server script

Edit: I may have understood now, if i access the “inventory” before it changes it shows the preset on the module script, after it gets changed it shows the changes, but still a bit confused on how to implement that, like ok cool, i can change stuff without changing the module script meaning that i can do that without inserting attributes or stuff like that

1 Like

but how i can link it to the player? like doing this only changes the current table (aka the inventory one) which as i understood can be accessed to every script right?

I use profile service so I have one large table that contains each player who has their own inventory, and this gives me two approaches.
You can either use the original idea of just firing an event from the client, or what I’ve been suggesting of auto-updating the table with the index metamethod.

So if you have a table containing all of the players (and their inventories), then you could do this:
With the inventory, add a proxy inventory for each player.
Every time you want to add or remove something in the inventory, do the changes to their proxy.
This is the easiest to do with profile service, but can still be done without.

Then all you have to do is fire that for the client where the changes occurred.

The reason why I personally prefer to use metatables and proxy’s is so that the changes automatically reflect to the client without having to refresh or anything. It’s just a user experience thing so don’t feel forced to have to use metatables for your inventory if you don’t want to.

i may sound stupid but im more of an example guy instead of the long responses, so to do the index thing method i have to use the profile service? a quick example on how i can use them? in the meanwhile ima check on the documentation, if i manage to understnad it ima edit this

is it a module script of datastore?

i would like to do the index metamethod but that is kinda challenging to me, got any advice to get started on small stuff?