I made a sort of mod system which you have these “mods” to modify your stats, the thing is that i don’t know where to put the folder of the inventory to make it work, i saw that people often use serverstorage but i don’t know how to access stuff in there using a local script
You should place the folder in replicated storage,
since both the server and the client can access everything placed inside of it
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.
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)
?
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)
what do you mean with the __NewIndex method?
any example code or the documentation page?
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.
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.
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
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})
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
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
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
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.