The error I’m getting is very clearly saying I cant store an instance in the DataStore, but I’m not trying to store an instance, only the properties.
Module function:
function module.SaveData(player, plot, ID)
--// player : the player
--// plot : the 'BuildPlot'
local PlayerData = {}
local BaseCFrame = game.Workspace.Plots[plot.Name].BuildPlot.CFrame
for _, item in pairs(plot:GetChildren()) do
if item:IsA("Model") then
table.insert(PlayerData, {
item.Name;
table.pack(item.PrimaryPart.CFrame:GetComponents())
})
elseif item:IsA("MeshPart") or item:IsA("Part") or item:IsA("UnionOperation") then
table.insert(PlayerData, {
item.Name;
table.pack(item.CFrame)
})
end
end
game.ReplicatedStorage.Datastore.SaveHouse:FireServer(ID, PlayerData)
end
Server:
local DS = game:GetService("DataStoreService"):GetDataStore("FYT2-Build-Plots")
game.ReplicatedStorage.Datastore.SaveHouse.OnServerEvent:Connect(function(ID, Data)
local succ, err = pcall(function()
DS:SetAsync(ID, Data)
end)
if succ then print("Saved!") return true elseif not succ then warn(err) return end
end)
game.ReplicatedStorage.Datastore.GetData.OnServerEvent:Connect(function(ID)
local succ, err = pcall(function()
DS:GetAsync(ID)
end)
if succ then print("Got data!") return DS:GetAsync(ID) elseif not succ then warn(err) return end
end)
I don’t think you can directly convert a CFrame to a string. Instead, you could try storing a CFrame in another table with each of its components converted to a string. (if you don’t really need the orientation, just serialize and use the object’s position instead.)
local function serializeCFrame(cf)
return {cf:GetComponents()} --//Add this to the user's data
end
I used the tostring() value and it saved and loaded for me,
local DS = game:GetService("DataStoreService")
local store = DS:GetDataStore("Data")
local string = tostring(workspace.Dummy.LeftFoot.CFrame)
store:SetAsync("1Minecraft0954",string)
When I removed the tostring() from the equation, Roblox produced error 104: Cannot store CoordinateFrame in data store.
They’re RemoteEvents though. RemoteEvents always pass through the player calling the remote as the first parameter, so when you index ID, you’re actually indexing the player, which is of type Instance.
Just add .UserId at the end in both OnServerEvent functions and see if it yields you success.
Ah yes, I just realised what the issue is I think. As Sharpie mentioned, client - server remote events send the player who sent the information. Therefore, the server is reading the player who sent it, then their ID. So I think your server is trying to store the player’s ID instead of the data you want.
I think you can safely remove ID from the client’s remoteevent.
This might just be my issue at this point, but I did save the house, but it’s saying the data is nil.
Again, could be something totally unrelated.
if data == nil then
print("No house data for plot!")
data = game.ReplicatedStorage.Datastore.GetData:FireServer(StarterHouseID)
print("Sent request to load StarterHouse")
task.wait(2)
end
for _, item in pairs(data) do
--//Other code made useless bc for some reason the data is nil.
You can’t coerce a CFrame value into a primitive string value (such that it can be stored in a DataStore) by passing it as an argument to the “tostring()” global function, instead you’d need to compartmentalise the CFrame value into its components and then store those in a string/table value. For example.
local CFrameValue = workspace.Part.CFrame
local SerializedCFrame = table.pack(CFrameValue:GetComponents())
print(#SerializedCFrame) --this table value can be stored in a DataStore
This gets all of the components which describe the CFrame value and then packs them into a table with the “table.pack” table library function and then prints the number of components.
This serialized data can easily be reverted back into its deserialized state when loading by doing the following.
local DeserializedCFrame = CFrame.new(table.unpack(SerializedCFrame))
Here, we unpack the components with the “table.unpack” table library function and subsequently pass each as an argument to the CFrame class constructor function in order to construct a CFrame value out of its components.
The issue with this is when “FireServer()” is being called on the RemoteEvent instance named “SaveHouse” two optional values are being passed as arguments to the instance method, which means that on the listening side any callback function connected to the corresponding “OnServerEvent” of the same RemoteEvent instance will need to be defined with three explicitly declared parameters, one to handle automatically passed player object as you correctly stated, and two to handle the additional two values passed to “FireServer()”, as your suggestion ignored this second optional value passed to “FireServer()” it ends up being discarded (resulting in the callback not working as intended).
game.ReplicatedStorage.Datastore.SaveHouse:FireServer(ID, PlayerData) --two optional values passed to fireserver()
game.ReplicatedStorage.Datastore.SaveHouse.OnServerEvent:Connect(function(Player, ID, Data) --connected callback function must declare three parameters, one to handle the received player object and another two to handle the two optional values
game.ReplicatedStorage.Datastore.GetData:FireServer(StarterHouseID) --optional value passed to fireserver()
game.ReplicatedStorage.Datastore.GetData.OnServerEvent:Connect(function(Player, ID) --connected callback function must declare two parameters, one to handle the received player object and another to handle the optional value
Here’s how both “FireServer()” calls and corresponding “.OnServerEvent” events should be written. With this, the definitions of those callback functions can be changed to incorporate the player object which fired the server, if necessary.