i am trying to use a table to store loadout data, and i have 1 server-side script and multiple local scripts that access it.

the server-side script has the actual table stored, and sends, recieves and saves data from the local scripts through remote functions. on the server side script, there are a few events that: get a player's loadout data from a datastore when they connect, set their loadout data to default loadouts if they have no loadout data, and save their loadout data when they leave. the problem is so far, every time that the local scripts try to access the table using the remote function, it returns an error about the table being "nil", and invoking the function through the console during testing has had an identical result.

here's the entire server-side script:

local dataStore = game:GetService("DataStoreService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local serverLoadouts = {}

game.Players.PlayerAdded:Connect(function(player) -- gets the player's loadout presets when they connect, gives them empty presets if there's no data
	local userId = player.UserId
	print(userId.." connected, checking for loadout data")
	serverLoadouts[userId] = dataStore:GetDataStore("loadouts"):GetAsync(userId)
	if serverLoadouts[userId] then -- checks if the player has loadout information
		print("giving "..userId.." default loadout")
		serverLoadouts[userId] = {
			selectedLoadout = 1,
			survivorLoadout1 = {
				armour = "none",
				class = "none",
				equipment = "none",
				ppe = "none",
				primary = "none"
			};
			survivorLoadout2 = {
				armour = "none",
				class = "none",
				equipment = "none",
				ppe = "none",
				primary = "none"
			};
			survivorLoadout3 = {
				armour = "none",
				class = "none",
				equipment = "none",
				ppe = "none",
				primary = "none"
			};
			survivorLoadout4 = {
				armour = "none",
				class = "none",
				equipment = "none",
				ppe = "none",
				primary = "none"
			};
			survivorLoadout5 = {
				armour = "none",
				class = "none",
				equipment = "none",
				ppe = "none",
				primary = "none"
			};
			tideLoadout1 = {
				armour = "none",
				class = "none",
				equipment = "none",
				ppe = "none",
				primary = "none"
			};
			tideLoadout2 = {
				armour = "none",
				class = "none",
				equipment = "none",
				ppe = "none",
				primary = "none"
			};
			tideLoadout3 = {
				armour = "none",
				class = "none",
				equipment = "none",
				ppe = "none",
				primary = "none"
			};
			tideLoadout4 = {
				armour = "none",
				class = "none",
				equipment = "none",
				ppe = "none",
				primary = "none"
			};
			tideLoadout5 = {
				armour = "none",
				class = "none",
				equipment = "none",
				ppe = "none",
				primary = "none"
			}
		} -- ends the default loadout
		dataStore:GetDataStore("loadouts"):SetAsync(userId, serverLoadouts[userId]) -- gives the player "empty" loadouts
	end -- ends the if statement
end) -- ends the function PlayerAdded

game.Players.PlayerRemoving:Connect(function(player)
	local userId = player.UserId
	print(userId.." disconnecting, saving loadout data")
	dataStore:GetDataStore("loadouts"):SetAsync(userId, serverLoadouts[userId]) -- saves any possible loadout changes when a player leaves
end)

replicatedStorage.returnServerLoadouts.OnServerInvoke = function(clientId)
	print("sending "..clientId.." loadout data")
	return serverLoadouts[clientId]
end -- remote function for sending the server's loadout data to the client

replicatedStorage.returnClientLoadouts.OnServerInvoke = function(clientLoadout, clientId)
	print("recieving "..clientId.." loadout data")
	serverLoadouts[clientId] = clientLoadout
end -- remote function to send client loadout data to the server

and every local script has

local function getLoadout() 
	print(game.Players.UserId.." recieving loadout data from server")
	return game:GetService("ReplicatedStorage").returnServerLoadouts:InvokeServer(game.Players.UserId)
end -- invokes the server, returns loadout data

to get the loadout data, and something similar to

local function setLoadout(slot, itemName)
	print(userId.." setting "..slot.." to "..itemName)
	local userLoadout = getLoadout()
	userLoadout["survivorLoadout"..userLoadout.selectedLoadout][slot] = itemName
	sendLoadout:InvokeServer(userLoadout, userId)
end -- gets the loadout, changes it according to parameters, sends it back

to change the loadout data.

any idea what's going on?