Datacore | save your data in many ways

Have you ever wanted a datastore service better than roblox current one?

Well you are in luck!

This module will make your life a million times better when dealing with datastore!

Features:

  • Able to save data in many kinds of value: strings, numbers, boolean!
  • You can use functions to use/delete/create data
  • Thing called “Slots” is where you can save many data in one Value!

How to Use:

  • First of all get the module here
  • Second of all you require the module script in your main script!
  • Third you can use all functions listed below:
datacore.CreateData(DataType: "StringValue"|"NumberValue"|"IntValue"|"BoolValue", Name: string, plr: Player, Value: number|string|boolean, parent: Instance?) -- this will allow you to create a data
datacore.SaveData(DataName: string, Slot: number?, DataForPlayer: Player) -- this will allow you to save data
datacore.LoadData(DataName: string, Slot: number?, DataForPlayer: Player) -- this will allow you to load data
datacore.GetData(DataName: string, Slot: number?, DataForPlayer: Player) -- this will allow you to gather the value the data is saved, this is mainly for debugging
datacore.GetDataObject(DataName: string, DataForPlayer: Player) -- this is similar to the one above, but instead will return in an object instead of the value, this is mainly for debugging
datacore.ChangeData(DataName: string, Slot: number?, DataForPlayer: Player, DataToChange: number|string|boolean) -- this will allow you to change the data, so basically like the save data, but instead it will change the data (like the function name)
datacore.DeleteData(DataName: string, Slot: number?, DataForPlayer: Player) -- this will allow you to delete the data
datacore.DeleteDataObject(DataName: Instance, DataForPlayer: Player) -- basically like getdataobject but it deletes it instead of getting it

you can see the code here:

local Data = {}

-- DATACORE MADE BY ALI
-- OPEN SOURCE, MODULE FOR FREE

--\\ Settings \\--
local DataSlots = 2 -- The Amount of slots where you can save Data
local DefaultDataSlotSaving_Loading = 1 -- the default slot to save/load data from

--\\ Variables \\--
local Datastore = game:GetService("DataStoreService")
local CustomData = Datastore:GetDataStore("CustomData")
local Http = game:GetService("HttpService")

--\\ functions \\--

function Data.CreateData(DataType: "StringValue"|"NumberValue"|"IntValue"|"BoolValue", Name: string, plr: Player, Value: number|string|boolean, parent: Instance?)
	if typeof(Name) ~= "string" then
		error(`Invalid Name, it must be a string`)
		return
	end
	if not game:GetService("Players"):FindFirstChild(plr.Name) then
		error(`Invalid Player, there must be a Player`)
		return
	end
	if DataType ~= "StringValue" then
		if DataType == "NumberValue" or DataType == "IntValue" or DataType == "BoolValue" then
			print("Succeed")
		else
			warn(`DataType is invalid, it must be a StringValue`)
		end
	elseif DataType ~= "NumberValue" then
		if DataType == "StringValue" or DataType == "IntValue" or DataType == "BoolValue" then
			print("Succeed")
		else
			warn(`DataType is invalid, it must be a NumberValue`)
		end
	elseif DataType ~= "IntValue" then
		if DataType == "NumberValue" or DataType == "StringValue" or DataType == "BoolValue" then
			print("Succeed")
		else
			warn(`DataType is invalid, it must be a IntValue`)
		end
	elseif DataType ~= "BoolValue" then
		if DataType == "NumberValue" or DataType == "IntValue" or DataType == "StringValue" then
			print("Succeed")
		else
			warn(`DataType is invalid, it must be a BoolValue`)
		end
	end
	
	if DataType == "StringValue" and typeof(Value) ~= "string" then
		warn('Invalid value for '..Name..', it must be a string ("this is a string")')
		return
	elseif DataType == "BoolValue" and typeof(Value) ~= "boolean" then
		warn('Invalid value for '..Name..', it must be a boolean ("true" or "false")')
		return
	elseif DataType == "NumberValue" and typeof(Value) ~= "number" then
		warn('Invalid value for '..Name..', it must be a number (3.6)')
		return
	elseif DataType == "IntValue" and typeof(Value) ~= "number" and math.round(Value) ~= Value then
		warn('Invalid value for '..Name..', it must be a integer (1)')
		return
	end
	
	if not parent then parent = script end
	
	if parent:FindFirstChild(Name) then
		warn(`Data already exists`)
		return
	end
	
	local NewFolderData
	
	if script.Data:FindFirstChild(plr.Name) then
		NewFolderData = script.Data:FindFirstChild(plr.Name)
	else
		NewFolderData = Instance.new("Folder", script.Data)
		NewFolderData.Name = plr.Name
	end
	
	local NewData = Instance.new(DataType, parent)
	NewData.Name = Name
	NewData.Value = Value
	
	local NewDataFolder = Instance.new("ObjectValue", NewFolderData)
	NewDataFolder.Name = Name
	NewDataFolder.Value = parent
	
	
	return NewData
end


function Data.SaveData(DataName: string, Slot: number?, DataForPlayer: Player)
	if typeof(DataName) ~= "string" then
		warn("Invalid Name, it must be a string")
		return
	end
	local DataParent = script.Data:FindFirstChild(DataForPlayer.Name):FindFirstChild(DataName).Value
	local DataInstance = DataParent:FindFirstChild(DataName)
	
	if not DataInstance then warn("Unable to find data") return end
	
	if not Slot then Slot = DefaultDataSlotSaving_Loading end
	
	local s, e = pcall(function()
		CustomData:SetAsync("Value_"..DataInstance.Name.."_Slot_"..tostring(Slot).."_UserId_"..DataForPlayer.UserId, DataInstance.Value)
	end)
	
	if e then
		warn("There is an error saving Data")
		warn(e)
	elseif s then
		print("Succesfully Saved Data")
	end
	
	return
end

function Data.LoadData(DataName: string, Slot: number?, DataForPlayer: Player)
	if typeof(DataName) ~= "string" then
		warn("Invalid Name, it must be a string")
		return
	end
	local DataParent = script.Data:FindFirstChild(DataForPlayer.Name):FindFirstChild(DataName).Value
	local DataInstance = DataParent:FindFirstChild(DataName)

	if not DataInstance then warn("Unable to find data") return end

	if not Slot then Slot = DefaultDataSlotSaving_Loading end

	local s, e = pcall(function()
		DataInstance.Value = CustomData:GetAsync("Value_"..DataInstance.Name.."_Slot_"..tostring(Slot).."_UserId_"..DataForPlayer.UserId)
	end)

	if e then
		warn("There is an error loading Data")
		warn(e)
	elseif s then
		print("Succesfully Loaded Data")
	end
	return
end

function Data.GetData(DataName: string, Slot: number?, DataForPlayer: Player)
	if typeof(DataName) ~= "string" then
		warn("Invalid Name, it must be a string")
		return
	end
	local DataParent = script.Data:FindFirstChild(DataForPlayer.Name):FindFirstChild(DataName).Value
	local DataInstance = DataParent:FindFirstChild(DataName)

	if not DataInstance then warn("Unable to find data") return end

	if not Slot then Slot = DefaultDataSlotSaving_Loading end
	
	local DataGotten = 0
	
	local s, e = pcall(function()
		DataGotten = CustomData:GetAsync("Value_"..DataInstance.Name.."_Slot_"..tostring(Slot).."_UserId_"..DataForPlayer.UserId)
	end)

	if e then
		warn("There was an error Getting Data")
		warn(e)
	elseif s and DataGotten then
		print("Succesfully Got Data")
		return DataGotten
	elseif DataGotten == nil then
		return "Found no data!"
	else
		return DataGotten
	end
end

function Data.GetDataObject(DataName: string, DataForPlayer: Player)
	if typeof(DataName) ~= "string" then
		warn("Invalid Name, it must be a string")
		return
	end
	local DataParent = script.Data:FindFirstChild(DataForPlayer.Name):FindFirstChild(DataName).Value
	local DataInstance = DataParent:FindFirstChild(DataName)

	if not DataInstance then warn("Unable to find data") return end
	
	return DataInstance
end

function Data.ChangeData(DataName: string, Slot: number?, DataForPlayer: Player, DataToChange: number|string|boolean)
	if typeof(DataName) ~= "string" then
		warn("Invalid Name, it must be a string")
		return
	end
	local DataParent = script.Data:FindFirstChild(DataForPlayer.Name):FindFirstChild(DataName).Value
	local DataInstance = DataParent:FindFirstChild(DataName)
	
	local DataType = DataInstance.ClassName
	
	if DataType ~= "StringValue" then
		if DataType == "NumberValue" or DataType == "IntValue" or DataType == "BoolValue" then
			print("Succeed")
		else
			warn(`DataType is invalid, it must be a StringValue`)
		end
	elseif DataType ~= "NumberValue" then
		if DataType == "StringValue" or DataType == "IntValue" or DataType == "BoolValue" then
			print("Succeed")
		else
			warn(`DataType is invalid, it must be a NumberValue`)
		end
	elseif DataType ~= "IntValue" then
		if DataType == "NumberValue" or DataType == "StringValue" or DataType == "BoolValue" then
			print("Succeed")
		else
			warn(`DataType is invalid, it must be a IntValue`)
		end
	elseif DataType ~= "BoolValue" then
		if DataType == "NumberValue" or DataType == "IntValue" or DataType == "StringValue" then
			print("Succeed")
		else
			warn(`DataType is invalid, it must be a BoolValue`)
		end
	end

	if DataType == "StringValue" and typeof(DataToChange) ~= "string" then
		warn('Invalid value for '..DataInstance.Name..', it must be a string ("this is a string")')
		return
	elseif DataType == "BoolValue" and typeof(DataToChange) ~= "boolean" then
		warn('Invalid value for '..DataInstance.Name..', it must be a boolean ("true" or "false")')
		return
	elseif DataType == "NumberValue" and typeof(DataToChange) ~= "number" then
		warn('Invalid value for '..DataInstance.Name..', it must be a number (3.6)')
		return
	elseif DataType == "IntValue" and typeof(DataToChange) ~= "number" and math.round(DataToChange) ~= DataToChange then
		warn('Invalid value for '..DataInstance.Name..', it must be a integer (1)')
		return
	end

	if not Slot then Slot = DefaultDataSlotSaving_Loading end

	local s, e = pcall(function()
		CustomData:SetAsync("Value_"..DataInstance.Name.."_Slot_"..tostring(Slot).."_UserId_"..DataForPlayer.UserId, DataInstance.Value)
		DataInstance.Value = DataToChange
	end)

	if e then
		warn("There is an error saving Data")
		warn(e)
	elseif s then
		print("Succesfully Changed Data")
	end

	return
end

function Data.DeleteData(DataName: string, Slot: number?, DataForPlayer: Player)
	if typeof(DataName) ~= "string" then
		warn("Invalid Name, it must be a string")
		return
	end
	local DataParent = script.Data:FindFirstChild(DataForPlayer.Name):FindFirstChild(DataName).Value
	local DataInstance = DataParent:FindFirstChild(DataName)

	if not DataInstance then warn("Unable to find data") return end

	if not Slot then Slot = DefaultDataSlotSaving_Loading end

	local s, e = pcall(function()
		CustomData:SetAsync("Value_"..DataInstance.Name.."_Slot_"..tostring(Slot).."_UserId_"..DataForPlayer.UserId, nil)
	end)

	if e then
		warn("There was an error Deleting Data")
		warn(e)
	elseif s then
		print("Succesfully Got Data")
	end
end

function Data.DeleteDataObject(DataName: Instance, DataForPlayer: Player)
	if typeof(DataName) ~= "string" then
		warn("Invalid Name, it must be a string")
		return
	end

	local DataParent = script.Data:FindFirstChild(DataForPlayer.Name):FindFirstChild(DataName).Value
	local DataInstance = DataParent:FindFirstChild(DataName)

	DataInstance:Destroy()
end

return Data

Thats all you need to know!

if you have any problems with this module let me know and i will try to fix it!

3 Likes

I have come to ask same question as i have asked to others,

Which datastore do we compare this with for efficieny.

Datastore2?
ProfileService?

I am not asking this with bad intention, i need something that tells me, what advantages does this give me over the other 2 datastores?

Gave a quick read over the source code and I do not support the way data is being saved, like at all.

You can use DataStore2 which is no longer being updated (AFAIK) and still get a better result over this. I’m in no way trying to be rude to the effort you have put into this, but I really do not support the way you use instances to save and load your data.

But getting into the positive part, if I were to support saving and loading with instances, I would recommend this. But at the moment, the code is just simple data saving and loading.

this module may not be for efficiency!

But on the other hand, this module uses less complicated functions and uses, so anyone no matter how much they know about luau may understand it!

this module may be for simplicity than efficiency!

I’m sorry, but how is that easier than roblox regular data stores?

local xp = game:GetService('DataStoreService'):GetDataStore('XP')
xp:SetAsync(player.userid, 50)

compared to:

local xp = module.CreateData('NumberValue', 'XP', player, 0)
xp.Value = 50
module.SaveData('XP', 1, player)

(sorry if the code is wrong, this module is hard to understand)

1 Like

Relying on instance replication sounds very dangerous for any modern game involving gamepass gated content… going to be a huge headache if people aren’t getting what they paid for because :FindFirstChild() didn’t find a NumberValue instance in time. I would at least add some kind of recursion to your data fetching functions so they don’t immediately give up when the requested thing isn’t there.

Why not use dictionaries though? They’re easier to read through and diagnose, and you won’t have to rely on instancing data.

Like this person’s comment, you’d be looking at something like local xp = data['xp'] versus local xp = game:GetService("ServerScriptService").Data.Data[parent].xp.Value

i understand your issue but it is:

module.CreateData("NumberValue", "XP", player, 0, parent) -- the "parent" is optional, if you didnt added a parent that means it will be stored inside the module
module.ChangeData("XP", 1, player) -- this will change the data and save the changed data

edit: you can do

local xp = module.CreateData("NumberValue", "XP", player, 0, parent)
but it isnt really necesary (if you want to debug)

i will see if i can do dictionaries