function DataStore2.Combine(l,...)for g,c in pairs({...})do combinedDataStoreInfo[c]=l end end function DataStore2.ClearCache()DataStoreCache={}end function DataStore2.SaveAll(l)if DataStoreCache[l]then for l,g in pairs(DataStoreCache[l])do if g.combinedStore==nil then g:Save()end end end end function DataStore2.PatchGlobalSettings(l)for l,g in pairs(l)do assert(Settings[l]~=nil,"\078\111 \115\117\099\104 \107\101\121 \101\120\105\115\116\115\058 "..l)Settings[l]=g end end function DataStore2.__call(l,g,c)assert(typeof(g)=="\115\116\114\105\110\103"and typeof(c)=="\073\110\115\116\097\110\099\101",("\068\097\116\097\083\116\111\114\101\050\040\041 \065\080\073 \099\097\108\108 \101\120\112\101\099\116\101\100 \123\115\116\114\105\110\103 \100\097\116\097\083\116\111\114\101\078\097\109\101\044 \073\110\115\116\097\110\099\101 \112\108\097\121\101\114\125\044 \103\111\116 \123\037\115\044 \037\115\125"):format(typeof(g),typeof(c)))if DataStoreCache[c]and DataStoreCache[c][g]then return DataStoreCache[c][g]elseif combinedDataStoreInfo[g]then local l=DataStore2(combinedDataStoreInfo[g],c)l:BeforeSave(function(l)for g in pairs(l)do if combinedDataStoreInfo[g]then local N=DataStore2(g,c)local I=N:Get(nil,true)if I~=nil then if N.combinedBeforeSave then I=N.combinedBeforeSave(clone(I))end l[g]=I end end end return l end)local N=setmetatable({combinedName=g,combinedStore=l},{__index=function(g,c)return CombinedDataStore[c]or l[c]end})if not DataStoreCache[c]then DataStoreCache[c]={}end DataStoreCache[c][g]=N return N end local N={}N.Name=g N.UserId=c.UserId N.callbacks={}N.beforeInitialGet={}N.afterSave={}N.bindToClose={}N.savingMethod=SavingMethods[Settings.SavingMethod].new(N)setmetatable(N,DataStoreMetatable)local I,Y=Instance.new("\066\105\110\100\097\098\108\101\069\118\101\110\116"),false game:BindToClose(function()if not Y then spawn(function()c.Parent=nil end)I.Event:wait()end local l=N:Get(nil,true)for g,N in pairs(N.bindToClose)do N(c,l)end end)local o o=c.AncestryChanged:Connect(function()if c:IsDescendantOf(game)then return end o:Disconnect()print(N);(((N:SaveAsync()):andThen(function()print("\112\108\097\121\101\114 \108\101\102\116\044 \115\097\118\101\100 "..g)end)):catch(function(l)warn("\101\114\114\111\114 \119\104\101\110 \112\108\097\121\101\114 \108\101\102\116\033 "..l)end)):finally(function()I:Fire()Y=true end)delay(40,function()DataStoreCache[c]=nil end)end)if not DataStoreCache[c]then DataStoreCache[c]={}end DataStoreCache[c][g]=N return N end DataStore2.Constants=Constants return setmetatable(DataStore2,DataStore2)

function DataStore2.Combine(mainKey, ...)
	for _, name in pairs({...}) do
		combinedDataStoreInfo[name] = mainKey
	end
end

function DataStore2.ClearCache()
	DataStoreCache = {}
end

function DataStore2.SaveAll(player)
	if DataStoreCache[player] then
		for _, dataStore in pairs(DataStoreCache[player]) do
			if dataStore.combinedStore == nil then
				dataStore:Save()
			end
		end
	end
end

function DataStore2.PatchGlobalSettings(patch)
	for key, value in pairs(patch) do
		assert(Settings[key] ~= nil, "No such key exists: " .. key)
		-- TODO: Implement type checking with this when osyris' t is in
		Settings[key] = value
	end
end

function DataStore2.__call(_, dataStoreName, player)
	assert(
		typeof(dataStoreName) == "string" and typeof(player) == "Instance",
		("DataStore2() API call expected {string dataStoreName, Instance player}, got {%s, %s}")
		:format(
			typeof(dataStoreName),
			typeof(player)
		)
	)

	if DataStoreCache[player] and DataStoreCache[player][dataStoreName] then
		return DataStoreCache[player][dataStoreName]
	elseif combinedDataStoreInfo[dataStoreName] then
		local dataStore = DataStore2(combinedDataStoreInfo[dataStoreName], player)

		dataStore:BeforeSave(function(combinedData)
			for key in pairs(combinedData) do
				if combinedDataStoreInfo[key] then
					local combinedStore = DataStore2(key, player)
					local value = combinedStore:Get(nil, true)
					if value ~= nil then
						if combinedStore.combinedBeforeSave then
							value = combinedStore.combinedBeforeSave(clone(value))
						end
						combinedData[key] = value
					end
				end
			end

			return combinedData
		end)

		local combinedStore = setmetatable({
			combinedName = dataStoreName,
			combinedStore = dataStore,
		}, {
			__index = function(_, key)
				return CombinedDataStore[key] or dataStore[key]
			end
		})

		if not DataStoreCache[player] then
			DataStoreCache[player] = {}
		end

		DataStoreCache[player][dataStoreName] = combinedStore
		return combinedStore
	end

	local dataStore = {}

	dataStore.Name = dataStoreName
	dataStore.UserId = player.UserId

	dataStore.callbacks = {}
	dataStore.beforeInitialGet = {}
	dataStore.afterSave = {}
	dataStore.bindToClose = {}
	dataStore.savingMethod = SavingMethods[Settings.SavingMethod].new(dataStore)

	setmetatable(dataStore, DataStoreMetatable)

	local event, fired = Instance.new("BindableEvent"), false

	game:BindToClose(function()
		if not fired then
			spawn(function()
				player.Parent = nil -- Forces AncestryChanged to fire and save the data
			end)

			event.Event:wait()
		end

		local value = dataStore:Get(nil, true)

		for _, bindToClose in pairs(dataStore.bindToClose) do
			bindToClose(player, value)
		end
	end)

	local playerLeavingConnection
	playerLeavingConnection = player.AncestryChanged:Connect(function()
		if player:IsDescendantOf(game) then return end
		playerLeavingConnection:Disconnect()
		print(dataStore)
		dataStore:SaveAsync():andThen(function()
			print("player left, saved " .. dataStoreName)
		end):catch(function(error)
			-- TODO: Something more elegant
			warn("error when player left! " .. error)
		end):finally(function()
			event:Fire()
			fired = true
		end)

		delay(40, function() --Give a long delay for people who haven't figured out the cache :^(
			DataStoreCache[player] = nil
		end)
	end)

	if not DataStoreCache[player] then
		DataStoreCache[player] = {}
	end

	DataStoreCache[player][dataStoreName] = dataStore

	return dataStore
end

DataStore2.Constants = Constants

return setmetatable(DataStore2, DataStore2)
