Saving parts/models via datastore?

I’ve looked everywhere around the wiki and devforums to no answer. I’ve thought about using Save and Load Instance but since it’s deprecated it seems like it’d be inefficient and unreliable in the future.

What I’m going for is like how Theme Park Tycoon works. It saves all the models inside your ‘area’ and when you load it back up it pastes it all back to your area.

I don’t see any easy way of storing every single model and loading it back up without fail

11 Likes

Instead of trying to save the entire model structure in a data store, you should consider only saving relevant information.

An approach to this is to have all the possible model templates in your system parented somewhere where you can locate it again, then you only save i.e. the following information as a minimum:

  • The model’s identifier (i.e. its name or another way to uniquely identify the model)
  • The model’s location
  • The model’s rotation (if you allow rotating it)

As well as additional information if needed.
Loading back that information then lets you find the template model to clone and position as needed.

Looking at the big perspective, you can now save more than 100x (rough estimate, saying we’re not caring about each brick’s transparency, anchored state, cancollide state, color, surface types etc.) as many models before you hit the data limit.

9 Likes

So I’d have to save and load a bunch of values?

1 Like

With data stores, you design the format your data is saved with - so you can decide what’s needed, and what’s unneccessary information.

As an example of saving data, you’d save a table, following some convention you decide on.

DataStore:SetAsync("mySavedModel", {Id = "MyModel", Position = {X = 0, Y = 5, Z = 3}})

And as for loading it, this example could be used - I expect a container named “Models” with a model named “MyModel” exists within ReplicatedStorage for this code snippet, with a PrimaryPart set.

local data = DataStore:GetAsync("mySavedModel")

local newModel = ReplicatedStorage.Models:FindFirstChild(data.Id):Clone() -- Clones the template model. data.Id is 'MyModel'
newModel:SetPrimaryPartCFrame(CFrame.new(data.Position.X, data.Position.Y, data.Position.Z)) -- Moves the cloned model somewhere defined by our saved data.
newModel.Parent = workspace -- Could be parented elsewhere, although this code snippet is just an example to give you an idea of how to handle loading your model.

In order to handle a collection of models being saved and loaded, you could wrap it inside of an array for the saved datastore entry. :slightly_smiling_face:

18 Likes

When saving models, you would need to save multiple values to the datastore.

You would need to save the identifier of the model (some sort of clarification of which model you would need to fetch when loading), the model’s PrimaryPart’s XYZ position values and the XYZ of the PrimaryPart’s lookVector.

To implement this, simple add these values to a table / dictionary. You could use something similar to the following:

local Player = -- Pass the player to the function when saving.
local Datastore = game:GetService("DatastoreService"):GetDataStore("ModelData", "Version1")
local ModelData = {}
local Model = game.Workspace.Model
local PrimaryPart = Model.PrimaryPart
local PosVector = PrimaryPart.Position
local Direction = PrimaryPart.lookVector
ModelData.ModelName = Model.Name -- In this example this is unnecessary; however, you would need it for your system.
ModelData.Position = {PosVector.x, PosVector.y, PosVector.z, Direction.X, Direction.Y, Direction.Z}
Datastore:SetAsync(Player.UserId ,ModelData)

For loading, you can just unpack the data using the following.

local Player = -- Passed to the function.
local Datastore = game:GetService("DatastoreService"):GetDataStore("ModelData", "Version1")
local ModelData = Datastore:GetAsync(Player.UserId)
local TargetModel = game.Workspace.Model -- You can use ModelData.ModelName if this is located differently / for multiple models.
local Information = ModelData.Position
local NewModel = TargetModel:Clone()
local Position = Vector3.new(Information[1], Information[2], Information[3])
local lookVector = Vector3.new(Information[4], Information[5], Information[6])
NewModel:SetPrimaryPartCFrame(Position, Position + (lookVector * 5))
NewModel.Parent = game.Workspace

To save a series of models, you could do the same; however, use a for loop which adds the ModelData to a main table and then save that main table.

You will need to modify this script; however, it should provide a good outline for what you’re aiming to achieve.

6 Likes
1 Like

Since ‘Model’ is just a single model, would I have to repeat this process for every single model?

function savePlace(player)
	local modelData = {}
	for _, model in pairs(bases:FindFirstChild(player.Name).ItemHolder:GetChildren()) do
		local primaryPart = model.PrimaryPart
		local position = primaryPart.Position
		local direction = primaryPart.CFrame.lookVector
		modelData.ModelName = model.Name
		modelData.Position = {position.x, position.y, position.z, direction.X, direction.Y, direction.Z}
	end
	print('saving')
	modelDataStore:SetAsync(player.UserId, modelData)
	print(modelData)
end

It prints saving, but not modelData. So guessing modelData is a nil value or something? I put it in a for loop, to get all of the models that are located in the folder, but yeah not working :confused:

EDIT

function savePlace(player)
	local modelData = {}
	for _, model in pairs(bases:FindFirstChild(player.Name).ItemHolder:GetChildren()) do
		local primaryPart = model.PrimaryPart
		local position = primaryPart.Position
		local direction = primaryPart.CFrame.lookVector
		modelData.ModelName = model.Name
		modelData.Position = {position.x, position.y, position.z, direction.X, direction.Y, direction.Z}
	end
	print(modelData.ModelName)
	modelDataStore:SetAsync(player.UserId, modelData)
end

When it prints modelData.ModelName, it only prints 1 of the models. So I’m not sure if it’s putting all of the models inside the table or not

1 Like

One immediate flaw I see is that you’re replacing the values for all of the previous items in the loop. I recommend putting each item in its own table then putting that table inside of modelData.

function savePlace(player)
local modelData = {}
for _, model in pairs(bases:FindFirstChild(player.Name).ItemHolder:GetChildren()) do
	local modelDataHolder = {}		
	local primaryPart = model.PrimaryPart
	local position = primaryPart.Position
	local direction = primaryPart.CFrame.lookVector
	modelDataHolder.ModelName = model.Name
	modelDataHolder.Position = {position.X, position.Y, position.Z, direction.X, direction.Y, direction.Z}
	table.insert(modelData, modelDataHolder)
	--modelData.ModelName = model.Name
	--modelData.Position = {position.X, position.Y, position.Z, direction.X, direction.Y, direction.Z}
end
modelDataStore:SetAsync(player.UserId, modelData)

end

This is what I did, but it now prints nil if I put a print above the SetAsync for modelData.ModelName. Not really used to working with this kinda stuff so not sure where I’m going wrong

function savePlace(player)
local modelData = {}
for _, model in pairs(bases:FindFirstChild(player.Name).ItemHolder:GetChildren()) do
local modelDataHolder = {}
local primaryPart = model.PrimaryPart
local position = primaryPart.Position
local direction = primaryPart.CFrame.lookVector
modelDataHolder.ModelName = model.Name
modelDataHolder.Position = {position.X, position.Y, position.Z, direction.X, direction.Y, direction.Z}
modelData.ModelName = modelDataHolder.ModelName
modelData.Position = modelDataHolder.Position
end
modelDataStore:SetAsync(player.UserId, modelData)
end

function loadPlace(player)
	local modelData = modelDataStore:GetAsync(player.UserId)
	local targetModel = modelData.ModelName
	local information = modelData.Position
	local newModel = targetModel:Clone()
	local position = Vector3.new(information[1], information[2], information[3])
	local lookVector = Vector3.new(information[4], information[5], information[6])
	newModel:SetPrimaryPartCFrame(position, position + (lookVector * 5))
	newModel.Parent = bases:FindFirstChild(player.Name).ItemHolder
end

I believe I’ve fixed the saving problem. Now I can’t seem to fix the loading. Error is on this line

local newModel = targetModel:Clone()

attempt to call method ‘Clone’ (a nil value)

But when I print(targetModel) it prints one the models, so it can’t be nil? Should it be put in a for loop?

modelData.ModelName would be invalid as you have to go through every table in modelData. You’re currently trying to fetch the information from the table holding all the other tables.

Also, targetmodel would just be the model’s name, not the model itself. You cannot clone it without retrieving the actual model matching the modelname.

How would I do that?

EDIT Wait nvm

newModel:SetPrimaryPartCFrame(position, position + (lookVector * 5))

Error Unable to cast Vector3 to CoordinateFrame

the argument it requires is a cframe value
you gave it the values with which you would of constructed said cframe

Yea I know that

what I’m saying is, didn’t you mean to do

newModel:SetPrimaryPartCFrame(CFrame.new(position, position + (lookVector * 5)))

1 Like
    function loadPlace(player)
    	local playersBase = bases:FindFirstChild('EmptyBase')
    	playersBase.Name = player.Name
    	local modelData = modelDataStore:GetAsync(player.UserId)
    	local targetModel = modelData.ModelName
    	local information = modelData.Position
    	local newModel = items:FindFirstChild(targetModel):Clone()
    	local position = Vector3.new(information[1], information[2], information[3])
    	local lookVector = Vector3.new(information[4], information[5], information[6])
    	newModel:SetPrimaryPartCFrame(CFrame.new(position, position + (lookVector * 5)))
    	newModel.Parent = bases:FindFirstChild(player.Name).ItemHolder
    end

Okay so now it seems to be getting there. But it only loads in one model, instead of all of the models (not sure if it’s not saving all of the models, or not loading all of the models

EDIT
it is definitely saving all of the models. But I’m not sure how to get all of the models out of the data store to load in

1 Like

are you sure it’s saving all the models? after checking the savePlace function you showed us above, each time it goes through a model it sets values for the same keys in modelData, namely modelData.ModelName and modelData.Position
what you should do is have an additional entry in modelData, for each model you iterate through, with their (unique) name and position, and when loading, to iterate through modelData for each entry of such a model
what I believe is happening is that each time it saves, it goes through all the models but each time one is saved, it replaces the previous one