Don't know where to store inventory stuff

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?

No, all you need is a table storing each player’s inventory. This could work with the regular datastore if you are planning on saving the inventories. Also you should store it in a module script so that any script should access it.

Whenever a player is added, add their inventory and their proxy to the table, and when they leave, remove it from the table.

And yes, profile service is a module that uses data store.

An example of this:

local DataStoreService = game:GetService("DataStoreService")

local InventoryStore = DataStoreService:GetDataStore("Inventory") -- Example datastore

local InventoryTable = {}

local function sendInventoryToClient(self, index, value)
	
end

local function addPlayerToTable(plr) -- Would be called by a script on player added
	local InventoryData = InventoryStore:GetAsync() -- returns table
	local InventoryProxy = setmetatable({}, {__newindex = function(self, index, value) sendInventoryToClient(self, index, value) end})
	
	InventoryTable[plr] = {InventoryData, InventoryProxy}
end

local function removePlayerfromTable(plr)
	InventoryTable[plr] = nil
end

-- Now any script can change each player's proxy and when they do, the client event will be fired

return InventoryTable

And my advice to you would be to follow the original suggestions of just firing an event to the server every time you want to access the inventory for that player, as that still functions just fine, and you could still always switch to my method of automatic updating at a later point when you understand metatables better.

ill let you know tommorrow, it’s late for me, gonna edit this once im back

1 Like

ok, so i should put a “folder” inside server storage for the basic stuff right? and after that how can i make it add stuff? like if the player has 3 copies, how can i make it add to the server?

how can i “access” them using different scripts tho, or is it like a table? can be used ONLY inside the script where it’s defined

wait is that a module script? cause that is so maybe i found the solution

i tried to do some tests but i can’t make it work, i know this is an example code but i think i have the same problems, rn i may switch to replicated storage having physical values but that would be a waste of time since you are supposed to do own more than one stuff

i understood the logic behind but i can’t put it to practical use, i understood that everything gets saved on the table and then when needed im supposed to change the table but i dont know how to properly use it, and where to put the module script if in replicated storage or serverstorage/serverscriptservice and then make that the client fires an event and when the script detects the event it updates the table/inventory, but doing that i still don’t know how to implement a template, cause in my situation i already have a module script that contains the basic info of the items, my idea was that you have a folder inside your player (Game.Players) which is called inventory, each time you “obtain” an item/mod the game inserts a string value inside the folder with different tributes (containing the basic information), name of the string value being the item name and then the value of the string being his current level, don’t know if it is any exploitable tho and i would rather avoid having exploitable stuff since it is a somewhat critic stats which can easily break down the entire game

how do i change the proxy?

So to go through this a little more step by step, so to start, I would have a module script in ServerScriptService that has a table of every single players inventory.

Also, I have a more efficient and more readable way of tracking every single time each player’s data was changed.

I am not going to write the code for you, but instead give you a template of a system that works for saving each player’s inventory.

-- WITHOUT PROXYS
local module = {}

module.Profiles = {}

return module

This is where we will insert every player and their inventory. I recommend doing this instead of values since you don’t have to do instance.new every time a player joins the game, and it is easier to use keys to access the data.

I would parent the module to a folder known as “DataSave”, so that we know that this is where a module holding everybody’s saves are.

Next, I would create a server script in the same folder that manages the data that goes into the profile holding module.

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")

local InventoryDataStore = DataStoreService:GetDataStore("Inventory")

local DataHolder = require(script.Parent.Profiles)

These services are going to be uses to input each player’s inventory save into the module.

I am not going to go into the nitty-gritty of using profile service or data store, as there are already plenty tutorials on that, and I figure you already implemented one of the two into your game already.

So now we can use player added and player removed to add the player’s data save into the module.

local Players = game:GetService("Players")
local DataStoreService = game:GetService("DataStoreService")

local InventoryDataStore = DataStoreService:GetDataStore("Inventory")

local DataHolder = require(script.Parent.Profiles)

Players.PlayerAdded:Connect(function(plr)
	local success, err = pcall(function()
		local PlayerInventorySave = InventoryDataStore:GetAsync(plr.UserId) -- Just getting their key which is their UserId so that we don't mix up data
		DataHolder.Profiles[plr].Inventory = PlayerInventorySave or {} -- Now we are adding their save to the table
	end)
end)

Again, I’m not going to go into the nitty-gritty of using datastore service or profile service, but the important thing that I’m doing here is adding the player’s save to the module so that it could be accessed by any script. The “or {}” just means that if the player’s data was equal to nil (had no data), then we would just insert a blank table.

Now for the player removed, we would just do the opposite.

Players.PlayerRemoving:Connect(function(plr)
	DataHolder.Profiles[plr] = nil
end)

All we are doing is removing their data from the server.

Now I feel like this is the big question: How can we actually access the data from any script.
Well, now that’s very easy because since their inventory is already stored in a module, any time we want the data, we just require it.

I personally have another step that I usually take, and it’s just personal preference.

I will have another module that handles what goes on in each players inventory. I name that module, “DataFunctions” as it simply stores all the functions for the data.

For example, this is how it would work with the inventory:

function DF.AddItemToInventory(player, item)
	local UserInventory = ProfileManager.Profiles[player].Data.Inventory -- you would just switch this up to wherever your table was if you were using DataStoreService
	ProfileManager.Profiles[player].Data.Inventory[#UserInventory + 1] = item
	return ProfileManager.Profiles[player].Data.Inventory
end

So from every ServerScript, let’s say the user earned a sword, I would just do.

local DataFunctions = require(script.Parent.DataFunctions)

-- Lets pretend that the user did some event to earn a sword

DataFunctions.AddItemToInventory(plr, sword)

Now, the next question would be, how do we send the inventory to the client every time a change happens if we are not using proxy’s. Well the answer is simple.
Since we already have a module function that adds the item to the inventory, inside that function, we could just fire an event that allows the client to update their inventory.

-- This is our data functions module

local RS = game:GetService("ReplicatedStorage")
local UpdateInventoryEvent = RS.UpdateInventory -- This event will tell the client to update their inventory.

function DF.AddItemToInventory(player, item)
	local UserInventory = ProfileManager.Profiles[player].Data.Inventory -- you would just switch this up to wherever your table was if you were using DataStoreService
	ProfileManager.Profiles[player].Data.Inventory[#UserInventory + 1] = item
    UserInventory = ProfileManager.Profiles[player].Data.Inventory
    UpdateInventoryEvent:FireClient(player, UserInventory)
	return UserInventory
end

Now the client has access to a new inventory every time it is updated.

If you are wondering what it would look like from the client, then here you go:

I recommend in StarterPlayerScripts having ONE script that manages the updating the inventory.

local RS = game:GetService("ReplicatedStorage")
local UpdateInventoryEvent = RS.UpdateInventory

local currentPlayerInventory = {} -- This is the table that we will be updated every time for the client.

UpdateInventoryEvent.OnClientEvent:Connect(function(updatedInventory)
	currentPlayerInventory = updatedInventory
end)

The reason why I recommend having only one local script that holds this information, is because you aren’t confused who has the latest information or what-not. We are making sure that this local script ALWAYS has up-to-date information on the player’s inventory.

So finally, how do we get this information anywhere on the client that needs it?
We just fire a bindable event every time the inventory is updated.
It would look something like this:

local RS = game:GetService("ReplicatedStorage")
local UpdateInventoryEvent = RS.UpdateInventory
local SendInventoryToClientScripts = RS.SendInventory

local currentPlayerInventory = {} -- This is the table that we will be updated every time for the client.

UpdateInventoryEvent.OnClientEvent:Connect(function(updatedInventory)
	currentPlayerInventory = updatedInventory
	SendInventoryToClientScripts:Fire(currentPlayerInventory)
end)

Now from any client, we could just do a

local Inventory = {}

RS.SendInventory.Event:Connect(function(newInventory)
   inventory = newInventory
end)

Alright to wrap this up, the reason why I told you to use proxy’s and metatables originally because I was assuming that you had an inventory system that was being updated randomly, and __newindex would allow you to update your inventory to the clients each time.

But if we have one localized area where we update the inventory, we could still send that data instantaneously without requiring metatables.

Sorry for the long reply.

1 Like

im gonna do a test using like a button that gives me an item and then see what happens

1 Like

what does it do? like i never used the # thing before

and now i kinda understood what you are doing with the bindable event, basically this script being the first one, and when it gets the info it fires it to everyone else that use the .event:Connect()… that’s something unique that i never tried before

The # is just counts the number of items in a table.

Use replicated storage and global inventories, when player joins add new folder and when he leaves remove it, don’t worry about data loss, use autosave and update async to prevent it

the thing is that i want to make it non-exploitable since it has some data that can break the game

tbh why doing all that? isn’t table.insert() more efficient? i got the same result using table.insert instead of that, and without table.insert() i couldn’t make it work

By the way i managed to make it work by doing a stupid test

Exploiters cannot edit replicated storage values, they can only see them

They more or less achieve the same thing, but I directly index the object since it is more “dictionary-proof” if you would put it like that, but I believe both would work here.

cheaters can’t change values, they can see them, but that doesn’t matter pretty much

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