[UNSOLVED] Saving & Loading Models

Alright, at this point I’ve tried everything I know. How do I save Client-Sided Models to DataStores? I have templates of every model in ReplicatedStorage if that helps. I know there’s a chance it can be done. I tried sending a table to the server with every model’s Name and their PrimaryPart’s Position, so I could then use that data to copy the templates with that name and pivot them to that position, but it didn’t work. I really need help with this.

2 Likes

So the client sided models are all just copies of something on the server side, just repositioned?
You should be able to just send that to the server (send the CFrame of the primary part as well as the model name, as you did), to save it to the datastore.

Note that you will need to process the CFrame, either to a table or to a string, in order to save it. You will also need to process it again when you try to load it, to convert it back into a cframe.

2 Likes

Could’ve changed the title to “Saving and loading models in a game”

As @SeargentAUS has said, you have to know how to serialise and deserialise data like these. Take a look at this tutorial:

2 Likes

So, I had tried these scripts.
SERVER

local datastoreservice = game:GetService("DataStoreService")
local mystore = datastoreservice:GetDataStore("mydatastore")

game.ReplicatedStorage.Events.GetTycoonData.OnServerEvent:Connect(function(plr)
	local playerid = "Player_" .. plr.UserId
	local data = mystore:GetAsync(playerid)
	
	if data then
		game.ReplicatedStorage.Events.ReturnTycoonData:FireClient(plr, data)
	end
end)


game.ReplicatedStorage.Events.SendTycoonData.OnServerEvent:Connect(function(player, dataTable)
	local playerid = "Player_" .. player.UserId
	local success, err = pcall(function()
		mystore:SetAsync(playerid, dataTable)
	end)
	if success then
		print("data saved!")
	else
		print("data failed to save!")
	end
end)

CLIENT

game.Players.PlayerAdded:Connect(function(plr)
	if plr == game.Players.LocalPlayer then
		print("welcome, me!")
		game.ReplicatedStorage.Events.GetTycoonData:FireServer()
		game.ReplicatedStorage.Events.ReturnTycoonData.OnClientEvent:Wait()
		game.ReplicatedStorage.Events.ReturnTycoonData.OnClientEvent:Connect(function(dataTable)
			for i, v in pairs(dataTable) do
				local util = game.ReplicatedStorage.TycoonElements[v.Name]:Clone()
				util.Parent = workspace.Tycoon
				util:PivotTo(v.CFrame)
			end
		end)
	end
end)

game.Players.PlayerRemoving:Connect(function(plr)
	local dataTable = {}
	for i, v in pairs(workspace.Tycoon:GetChildren()) do
		local dataElement = {}
		dataElement.Name = v.Name
		dataElement.CFrame = v.PrimaryPart.CFrame
		table.insert(dataTable, dataElement)
	end
	game.ReplicatedStorage.Events.SendTycoonData:FireServer(dataTable)
end)

Obviously, I’m doing something wrong. I know about the serialization, but can I use both Orientation and Position with PivotTo:()?

2 Likes

Also the “Client” isn’t exactly a LocalScript, but a ServerScript with the RunContext of Client. I tired it as a LocalScript, but it still didn’t work.

Use a local script.
I don’t think the client is going to detect itself on the player added nor on the player removing. Instead, just make it instantly act as though the player was added, and for the removing part - Periodically send the server the information, instead of trying to send it just before the client disconnected.

I would prefer if you actually take your time to read the tutorial I have linked in my previous post. Your code doesn’t seem like it applies knowledge from the tutorial.

And why are you handling the loading/saving of the data on the client?

1 Like

Periodically, as in send it every, say, 30 seconds?

what :no_mouth:
the datastore saving is literally on the server script

I guess whenever they confirm its placement. Make sure to add a delay / queue sorta system though, so they can’t just spam the remote

I’ll try out your suggestions and see if it works out. If it doesn’t, I’ll let you know. Thank you.

you can’t fire to the server from a server script, regardless of it being in Client Context.

E.G. You can’t call yourself from your own cellphone (I mean technically you go to voicemail lol, but you can’t speak into the mic and hear yourself on the speaker)

Yeah, you’re right. That does actually make more sense that way.

		game.ReplicatedStorage.Events.ReturnTycoonData.OnClientEvent:Wait()
		game.ReplicatedStorage.Events.ReturnTycoonData.OnClientEvent:Connect(function(dataTable)

Your loading doesn’t seem to work. You first connect a :Wait() function and then only connect a function to be ran when it is fired.

So when the remote event is fired, it only fires the:Wait() function. It wouldn’t fire the function connected. You can use a RemoteFunction instead which yields for input from the server, then use the data returned from the server to process to process it.

If something happens in one serverscript, and you want another script to pick up on it, you’d fire to a BindableEvent, rather than a RemoteEvent.

Those ones are client-exclusive/server-exclusive, depending on which type of script is running it.

In this case, though, since you’re listening for the client to do something and send info to the server, it’s best to keep it a RemoteEvent and fire from a LocalScript to the Server, then have the ServerScript listen and fire back.

(I didn’t mean to mark anything as a solution)
After messing around with your suggestion for a bit, it ALMOST worked, until I ran into another problem.


The line:
local util = game.ReplicatedStorage.TycoonElements[v.Name]:Clone()
In the LocalScript.
The full part:

game.ReplicatedStorage.Events.GetTycoonData:FireServer()
game.ReplicatedStorage.Events.ReturnTycoonData.OnClientEvent:Connect(function(dataTable)
	for i, v in pairs(dataTable) do
		local util = game.ReplicatedStorage.TycoonElements[v.Name]:Clone()
		util.Parent = workspace.Tycoon
		util:PivotTo(CFrame.new(v.CFrameX, v.CFrameY, v.CFrameZ))
		util.CFrame *= CFrame.Angles(math.rad(v.OrientX), math.rad(v.OrientY), math.rad(v.OrientZ))
	end
end)

I’m not sure what it’s supposed to mean by that. Is ‘v’ a number or something? Should I use ‘i’ instead? Edit: Nevermind, when I use ‘i’ it just says

Players.DisksjwYT.PlayerScripts.LocalScript:4: invalid argument #2 (string expected, got nil)

Usually when I use ‘v’ for tables it works, but not now, apparently.

No. It means, you are trying to index a table using a non-number value, which is using the player’s Name.

What does TycoonElements contain?

the templates

character limitigjdijbf

Do the templates have the same name as the player’s name?

no, they have the same name as the elements of the tycoon
im not sure how else to put it
“StoneDropper”
“OreSeller”
“StraightConveyor”