InstanceStore (Instance to Datastore)

InstanceStore

by LumberUniverse

currency-exchange (1)


Have you ever wanted to save instances to a datastore before? If you have then this module is the solution to that problem. At the moment almost everything except Unions should be supported (for MeshParts they have to be preexisting in the game for it to work). I had wanted to add support for Unions but Roblox doesn’t currently have a way to separate Unions on runtime.

Where to find it?

How does it work?

The module starts by grabbing the possible properties for the instance from MaximumADHD's (CloneTrooper1019) API dump. It then compares them to the default values and then saves the non-default ones.

Why?

This module can be used in games that can take advantage of saving instances like building games, roleplaying games, etc. (You might want to do some slight modifications like adding more properties to DontSave to decrease storage taken up)

How to use?

--[[
    This function converts the object to a table of properties
]]
Converter:ConvertToSaveable(Object : Instance, Parent : Instance, IncludeDescendants : Boolean)

-- e.g.
DataStore:SetAsync('Part', Converter:ConvertToSaveable(workspace.Baseplate, false))

--[[
    This function converts the table back into a object
]]
Converter:ConvertToInstance(Data : Table)

-- e.g.
Converter:ConvertToInstance(DataStore:GetAsync('Part')).Parent = workspace

Limits?

  • You can only store 4MiBs of data in a Roblox datastore (I’ve tried to reduce the storage amount that the module takes up as much as I can. You can combat this with a different data saving method)
  • The conversion can be a bit resource-intensive (haven’t had the chance to improve performance)
  • You can’t save Unions or save MeshParts that aren’t anywhere else e.i. aren’t in ReplicatedStorage or ServerStorage

Credits to MaximumADHD for the API dump

If you have any suggestions or recommendations feel free to reply to this thread and I’ll read it as soon as I can. Have a nice day :grinning:


Preview:

gNrIgSWVt5

Icons provided by icons8

39 Likes

At the end of the day my recommendation here is that you save bare minimum amounts of data. Each prefab should be representable by an id so that you just need to save an id, position and rotation to your DataStore. When you load up the data you can then go through the data, copy the prefab matching the id and set its coordinates accordingly. This’ll allow you more storage capacity.

It’s a nice resource but you probably don’t want to use it for a production build of your experience.

2 Likes

Right now it’s saving only data it has to and depending on the use case you can improve storage by not saving even more by placing the property name in the DontSave table

I understand. I’m still saying that you probably don’t want to use this in a production build.

InstanceStore, or rather the Converter module that’s linked in the thread, converts an instance and its properties into a serialisable format. At the core of the module, irrespective of its nuances, its intention is to serialise instances so it can be pushed to a DataStore. It’s not a specially tailored module or anything; it takes an instance, turns it into data and gives it back to be saved.

For building and data intensive experiences that want to save large quantities of placed models, you don’t want to simply convert an instance, its properties and its children into a serialisable format. The most major concern is data capacity; even if you omit properties, you’re still looking at a hefty size just to save one large model, so that’s not sufficient enough. That alone is enough to discourage its use in production, other issues notwithstanding.

Compatibility is an example of an “other issue” mentioned above. If you wanted to update an older model, the updated model will not be loaded for older players. They will instead receive an outdated copy when their data is loaded up, so the module lacks support for newer models. That’s a large red flag for experiences that at any point in time expect to update older models.

It’s significantly better, especially for experiences with building as either part of or directly the core gameplay loop, to tailor a system wherein they can associate prebuilt models with an id and save bare minimum amounts of data (position, rotation) for non-prefabs (e.g. building space primitives - floors, walls, ceilings). You aren’t saving any properties; only an association and coordinates.

It’s a nice resource but not one you should want to use in a production build unless you have a very specific use case that’s not solvable without serialising an instance.

2 Likes

I’m not entirely sure what you mean by newer models and older models but I completely understand your viewpoint and it’s definitely a good one. This module is more of an all-purpose module so doing something like that would focus on just the building section. Maybe I’ll alter it in the future to allow for this I’ll have to see (it wouldn’t be too difficult). Besides that, I’m working on some compression methods to get it even further down. The saving is already incredible and I suggest you see how much you can already save. As a quick estimate rn, it should handle 13k normal parts

it would be cool if you can have a parameter to put a table of string of the property that you wanna save so like
Converter:ConvertToSaveable(workspace.Baseplate,false,{“Position”,“Size”})
otherwise this module is cool

Suppose I’m the developer of a roleplay experience that allows players to place down models and I want whatever they place down to save. A player places down this janky looking table from an early gameplay build and it happens to save:

image

Now I want to update this table, give it some new colours or maybe even extra parts. So maybe later into development the table becomes this:

image

With just purely your converter, that first janky table will be saved to a player’s data. When I go to load it up, that same table will be loaded back - but that’s not the table I want to be appearing, it’s the second one that I want to appear now. Older players will not be able to get this new table though. It would require an unnecessarily large amount of effort on my part to see that the table they loaded is an old copy and then swap it out with the new one.

Instance properties should not be authoritative over the appearance of an asset in the game. An id that can be associated with a model instead and then be further configured with save data allows you the flexibility to update older models and save only a 1-5 digit number (depending on how many placeable assets you realistically see your experience having) to dictate what model should be loaded in the next play session which is significantly better than saving data on each individual part that comprises the model instead. It’s easier to work with and allows you more capacity.

As I’ve said in previous posts - I’m not necessarily knocking on your module, I think it’s a nice thing, but it just doesn’t fit in production use cases. Even with compression it’ll still hold inferior to a custom tailored system that works with id association. I appreciate the suggestion of testing it but objectively you will have significantly more capacity by association than by serialising individual parts.

A lot of building-based experiences may require more than 13K parts to be saved, going by your approximation. If you just use an id to associate with a model you don’t have to worry about how many parts you can save, only how much data an asset pair comprises of (the id, position and rotation). So instead of thinking in how many parts can be saved you’re thinking more in terms of how large one single “model marker” is and then how many of those can be saved. Example:

{
    id = 1,
    p = {x = 10.01, y = 3, z = 0},
    r = {x = 15, y = 0, z = 0},
}

This might be the marker for one model with an id of 1 and coordinates values up to 2 decimal places but you don’t know how many parts there are. It could just be 1 part or it could be a thousand parts. We don’t need to worry about part count though; just how many bytes this set takes up. It could even somehow be 13K parts, the estimated maximum your module can take without compression, but it still doesn’t have an affect on the capacity to store the above association.

The instance’s appearance with an id is decoupled from data beyond understanding which model we want to load (whichever model is assigned an id of 1) and where its coordinates are. Don’t have to worry about part appearance, hierarchy (parents and children) or any components I may want to add or remove from the model. It’s significantly more performant too; we’re just cloning a model that matches the entered id rather, so performance hinges on Roblox replication rather than how well a module can break down data and make instances from that data.

1 Like

my code doesn’t handle saving. So I’m having a really hard time understanding the issue with people only seeing the old model. You can override the old model with the new model and you’ll be able to load the stuff fine. It will be the new one that is shown.

And don’t get me wrong I 100% agree with all the statements I understand (just the old and new model. I think I know what you mean just not entirely)

I might make an id system. (is this something you would like/use or is it more of a suggestion)

1 Like

he basically means that you should have prepared models in your game. and the datastore is only supposed to save and load the changes. not the entire model property by property.
because once you do that, you can’t update old players models and data unless if you make a notoriously crazy script to identify the old model and replace it by a new model.

In conclusion. you shouldn’t save all the model with all of it’s properties and parts, but instead you prepare some templates or prefabs inside your game. and then when the player joins, you just copy that prefab or template from the game files and then you look up the changes that the player did on it through datastore.

that’s why it’s hard to make a general purpose datastore for instances, every game have a different way of handling things.

I believe colbert is an advanced developer so I think it’s more of a suggestion to improve and fix the design flaws in your module.

However you did a good job to make such thing, it’s just not efficient when it comes to the real world application :smiley:
Keep going!

1 Like

Thanks, I understand now :smiley:

Yeah, I definitely understood that he wasn’t trying to say I was bad or the module was bad. I just wanted to understand his comments completely so I can improve and hopefully release a better and more efficient module in the future.

1 Like

I would use this, but a lot of the models I need to be saved use unions.

UPDATE

The module now checks a folder for matching models and rather saves minimal data instead of saving all the children. This also means there is now support for unions.

If you would like to use this you need to place the models/unions in the ‘Models’ Folder under the module
image

Then you need to generate the ID for the module. You can use this code by selecting all the instances in the folder and pasting this code in the command bar.

for i, v in pairs(game.Selection:Get())do
v:SetAttribute('ModelID', v:GetAttribute('ModelID') or game.HttpService:GenerateGUID())
end

In-game you want to clone the models from the folder so they keep the ModelID attribute. If you require any assistance I’m more than happy to help.

(Update due to @colbert2677 @AlbertSeir thanks for showing interest)

5 Likes

This is a very useful plugin and will help me a lot. :heart:

hi!!! Im trying to make a tycoon but i keep getting this error:
image

and this is my script:

local dss = game:GetService("DataStoreService")
local ds = dss:GetDataStore("TycoonStore")

local Converter = require(game.ServerScriptService:WaitForChild("Converter"))


game.Players.PlayerAdded:Connect(function()
	local success, err = pcall(function()
		Converter:ConvertToInstance(ds:GetAsync('Model')).Parent = workspace
	end)
end)


game.Players.PlayerRemoving:Connect(function()
	local success, err = pcall(function()
		ds:SetAsync('Model', Converter:ConvertToSaveable(workspace.Tycoon, true))
	end)
	
	if success then
		print("Data Saved!")
	else
		warn(err)
	end
end)

if you could please help as soon as possible!!!

Unfortunately, this is not the right module for a Tycoon game as it purely saves the parts and loads them back in. You would want to work on a custom saving method or look at a premade system.

1 Like