local K={}K.Shutdown=false K.Initialized=false K.LoadedPlayers={}K.FlaggedData={}local N={}N.Timeout=3 N.Key=nil N.Cached={}N.Removal={}N.Files={}N.Internal={Set="\083\101\116\068\097\116\097";Get="\071\101\116\068\097\116\097";Load="\076\111\097\100\068\097\116\097";Save="\083\097\118\101\068\097\116\097",Spawn="\083\112\097\119\110\068\097\116\097";Watch="\087\097\116\099\104\068\097\116\097",Update="\085\112\100\097\116\101\068\097\116\097";Increment="\073\110\099\114\101\109\101\110\116\068\097\116\097"}local y=setmetatable({},{__index=function(K,N)K[N]=game:GetService(N)return K[N]end})local l=y.Players.LocalPlayer local L=script:WaitForChild("\077\111\100\117\108\101\115")local J=require(L:WaitForChild("\077\115\103\083\101\114\118\105\099\101"))local U=require(L:WaitForChild("\077\097\110\097\103\101\114"))local T=require(L:WaitForChild("\077\101\116\104\111\100\115"))local g=script:WaitForChild("\078\101\116\119\111\114\107")local s=g.SendData local D=g.RetrieveData local z=y.RunService:IsStudio()local h=y.RunService:IsServer()local e=y.RunService:IsClient()local p=-1 local q=6 local function P(...)local K={...}if h then if typeof(K[1])=="\073\110\115\116\097\110\099\101"and K[1]:IsA("\080\108\097\121\101\114")then local N=K[1]local y=K[2]table.remove(K,table.find(K,N))table.remove(K,table.find(K,y))s:FireClient(N,y,table.unpack(K))return true end local N=K[1]table.remove(K,table.find(K,N))s:FireAllClients(N,table.unpack(K))return true end local N=K[1]table.remove(K,table.find(K,N))s:FireServer(N,table.unpack(K))return true end local function I(...)local K={...}if h then if typeof(K[1])=="\073\110\115\116\097\110\099\101"and K[1]:IsA("\080\108\097\121\101\114")then local N=nil local y=K[1]local l=K[2]table.remove(K,table.find(K,y))table.remove(K,table.find(K,l))local L,J=pcall(function()N=D:InvokeClient(y,l,table.unpack(K))end)if L then return N end return false end return false end local N=K[1]table.remove(K,table.find(K,N))local y=s:InvokeServer(N,table.unpack(K))return y end local function F(K)local N=nil local l,L=pcall(function()N=y.Players:GetPlayerByUserId(K)end)if not l then return false end return N end function K.SetData(l,...)local L={...}local J=L[1]local g=L[2]local s=L[3]or false assert(typeof(J)=="\115\116\114\105\110\103","\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \075\101\121 \'\115\116\114\105\110\103\' \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(J).."\'"))assert(typeof(g)=="\116\097\098\108\101","\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \068\097\116\097 \'\116\097\098\108\101\' \111\114 \'\100\105\099\116\105\111\110\097\114\121\' \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(J).."\'"))U.wrap(function()if N.Files[J]~=nil then if N.Files[J].Event then N.Files[J].Event:Disconnect()end if N.Files[J].Task then N.Files[J].Task:Disconnect()end N.Files[J]=nil end g.___PlayingCards=false T.SetDefault(J,g)N.Files[J]={Default=g;Cache={};Tasks={};Event=nil,Sync=s}if s and not N.Key then N.Key=J end if s and h then local function l(y)while not K.LoadedPlayers[y.UserId]do U.wait()end P(y,N.Internal.Set,J,N.Files[J],s)end N.Files[J].Event=y.Players.PlayerAdded:Connect(function(K)l(K)end)for K,N in pairs(y.Players:GetPlayers())do l(N)end elseif e then K.Initialized=true end end)end function K.SetRemoval(K,...)local y={...}local l=y[1]local L=y[2]U.wrap(function()if typeof(l)=="\116\097\098\108\101"then while N.Key==nil do U.wait()end L=l l=N.Key end N.Removal[l]=L end)end function K.GetData(y,...)local L={...}local J=L[1]local T=L[2]local g=L[3]local s=nil local D=nil local z={}z.__Return=nil z.__Update=function()if e then if tonumber(T)~=l.UserId and(N.Key==J and s)then local K=I(s,N.Internal.Get,J,T,g)z.__Return=K return end end if g==nil then z.__Return=N.Files[J].Cache[T]return elseif g~=nil then z.__Return=N.Files[J].Cache[T][g]return end warn("\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \067\111\117\108\100 \110\111\116 \102\105\110\100 \100\097\116\097\044 \107\101\121 \'",J,"\' \097\110\100 \105\110\100\101\120 \'",T,"\' \097\110\100 \'",g,"\'")z.__Return=false return end s=F(J)if not s then s=F(T)end while N.Key==nil do U.wait()end if not J and(not T and(not g and e))then J=N.Key T=l.UserId g=nil elseif J~=nil and(not T and(not g and e))then g=J T=l.UserId J=N.Key elseif tonumber(J)and(J~=N.Key and(not T and not g))then g=nil T=tonumber(J)J=N.Key if not l then while K.LoadedPlayers[T]==nil do U.wait()end elseif l.UserId==tonumber(J)then while K.LoadedPlayers[T]==nil do U.wait()end end elseif tonumber(J)and(J~=N.Key and(T~=nil and not g))then g=T T=tonumber(J)J=N.Key if not l then while K.LoadedPlayers[T]==nil do U.wait()end elseif l.UserId==tonumber(J)then while K.LoadedPlayers[T]==nil do U.wait()end end end assert(typeof(J)=="\115\116\114\105\110\103","\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \075\101\121 \'\115\116\114\105\110\103\' \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(J).."\'"))assert(T~=nil,"\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \073\110\100\101\120 \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(T).."\'"))while N.Files[J]==nil do U.wait()end while N.Files[J].Cache[T]==nil do U.wait()end D=N.Files[J].Tasks[T]if not D then N.Files[J].Tasks[T]=U:Task(p)D=N.Files[J].Tasks[T]end D:Queue(z.__Update)local h=z.__Return~=nil and true or false return z.__Return,h end function K.UpdateData(y,...)local l={...}local L=l[1]local J=l[2]local T=l[3]local g=l[4]local s=""local D=nil local z=nil local e={}e.__Return=nil e.__Update=function()if typeof(T)=="\116\097\098\108\101"or T==nil then N.Files[L].Cache[J]=T if D and h then U.spawn(function()P(D,N.Internal.Update,L,J,T)end)end e.__Return=T local K=L..("\058"..(J.."\058\123\125"))U:FireKey(K,T)return elseif T~=nil then local K,y=pcall(function()N.Files[L].Cache[J][T]=g end)if not K then warn("\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \070\097\105\108\101\100 \116\111 \099\104\097\110\103\101\044 \101\114\114\111\114\058",y,debug.traceback())end if D and h then U.spawn(function()P(D,N.Internal.Update,L,J,T,g)end)end e.__Return=g local l=L..("\058"..(J..("\058"..T)))U:FireKey(l,g)return end e.__Return=false return end D=F(L)if not D then D=F(J)end if tonumber(L)and L~=N.Key then g=T T=J J=tonumber(L)L=N.Key while K.LoadedPlayers[J]==nil do U.wait()end end if typeof(T)~="\116\097\098\108\101"then while N.Files[L].Cache[J]==nil do U.wait()end end assert(typeof(L)=="\115\116\114\105\110\103","\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \075\101\121 \'\115\116\114\105\110\103\' \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(L).."\'"))assert(J~=nil,"\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \073\110\100\101\120 \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(J).."\'"))while N.Files[L]==nil do U.wait()end z=N.Files[L].Tasks[J]if not z then N.Files[L].Tasks[J]=U:Task(p)z=N.Files[L].Tasks[J]end z:Queue(e.__Update)local q=e.__Return~=nil and true or false return e.__Return,q end function K.IncrementData(y,...)local l={...}local L=l[1]local J=l[2]local T=l[3]local g=l[4]while N.Key==nil do U.wait()end if tonumber(L)and(L~=N.Key and J~=nil)then g=T T=J J=tonumber(L)L=N.Key while K.LoadedPlayers[J]==nil do U.wait()end end local s=K:GetData(L,J,T)if s and tonumber(g)then s=s+tonumber(g)return K:UpdateData(L,J,T,s)end return false end function K.WatchData(K,...)local y={...}local L=y[1]local J=y[2]local T=y[3]local g=y[4]while not N.Key do U.wait()end if L~=nil and(J==nil and(T==nil and(g==nil and e)))then g=L T="\123\125"J=l.UserId L=N.Key elseif L~=nil and(J~=nil and(T==nil and g==nil))then g=J T=L L=N.Key J=l.UserId elseif tonumber(L)and(J~=nil and(T==nil and g==nil))then g=J T="\123\125"J=tonumber(L)L=N.Key elseif tonumber(L)and(J~=nil and(T~=nil and g==nil))then g=T T=J J=tonumber(L)L=N.Key end assert(L~=nil)assert(J~=nil)assert(T~=nil)assert(typeof(g)=="\102\117\110\099\116\105\111\110")local s=L..("\058"..(J..("\058"..T)))U:ConnectKey(s,g)end function K.LoadData(l,...)if e then return end local L={...}local J=L[1]local g=L[2]local s=L[3]or false local D=nil while N.Key==nil do U.wait()end if tonumber(J)and J~=N.Key then pcall(function()D=y.Players:GetPlayerByUserId(J)end)s=g g=J J=N.Key else assert(typeof(J)=="\115\116\114\105\110\103","\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \075\101\121 \'\115\116\114\105\110\103\' \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(J).."\'"))assert(g~=nil,"\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \073\110\100\101\120 \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(g).."\'"))end if not N.Files[J]then warn("\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \070\097\105\108\101\100 \116\111 \115\097\118\101 \100\097\116\097\044 \107\101\121 \101\120\112\101\099\116\101\100\044 \103\111\116 \'",typeof(J),"\'")return false end local z,h=false,false local function q()while table.find(K.FlaggedData,g)do U.wait()end table.insert(K.FlaggedData,g)h,z=T:Load(J,g)N.Files[J].Cache[g]=h if D then U.wrap(function()K:UpdateData(J,g,h)end)end K:CalculateSize(J,g,"\076\111\097\100\101\100 \100\097\116\097")table.remove(K.FlaggedData,table.find(K.FlaggedData,g))U.spawn(function()if s then while N.Files[J].Cache[g]do U.wait()U.wait(300)K:SaveData(J,g)end end end)return z end local P=N.Files[J].Tasks[g]if not P then P=U:Task(p)N.Files[J].Tasks[g]=P end P:Queue(q)N.Files[J].Cache[g]=h return z end function K.SaveData(y,...)if e then return end local l={...}local L=l[1]local J=l[2]local g=l[3]or false local s=true if K.Shutdown and g then return end if tonumber(L)and L~=N.Key then g=J J=L L=N.Key s=false end if s then assert(typeof(L)=="\115\116\114\105\110\103","\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \075\101\121 \'\115\116\114\105\110\103\' \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(L).."\'"))assert(J~=nil,"\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \073\110\100\101\120 \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(J).."\'"))end if K.Shutdown then g=true end if not N.Files[L]then warn("\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \070\097\105\108\101\100 \116\111 \115\097\118\101 \100\097\116\097\044 \107\101\121 \101\120\112\101\099\116\101\100\044 \103\111\116 \'",typeof(L),"\'")return false end local D=false local function z()while table.find(K.FlaggedData,J)do U.wait()end table.insert(K.FlaggedData,J)local y=N.Files[L].Cache[J]if y==nil then return end U.spawn(function()if g then y.___PlayingCards=false if N.Files[L].Tasks[J]~=nil then if N.Files[L].Tasks[J]:Enabled()then N.Files[L].Tasks[J]:Disconnect()N.Files[L].Tasks[J]=nil end end if N.Files[L].Cache[J]~=nil then N.Files[L].Cache[J]=nil end end end)if g then for K,l in pairs(N.Removal[L])do y[l]=N.Files[L].Default[l]end end D=T:Save(L,J,y)if not K.Shutdown then K:CalculateSize(L,J,"\083\097\118\101\100 \100\097\116\097")end table.remove(K.FlaggedData,table.find(K.FlaggedData,J))return D end while table.find(K.FlaggedData,J)do U.wait()end local h=N.Files[L].Tasks[J]if h then pcall(function()h:Queue(z)end)return D end return false end function K.ClearData(y,...)if e then return end local l={...}local L=l[1]local J=l[2]local g=true if tonumber(L)and L~=N.Key then J=L L=N.Key g=false end if g then assert(typeof(L)=="\115\116\114\105\110\103","\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \075\101\121 \'\115\116\114\105\110\103\' \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(L).."\'"))assert(J~=nil,"\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \073\110\100\101\120 \101\120\112\101\099\116\101\100\044 \103\111\116 \'"..(typeof(J).."\'"))end if not N.Files[L]then warn("\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \070\097\105\108\101\100 \116\111 \115\097\118\101 \100\097\116\097\044 \107\101\121 \101\120\112\101\099\116\101\100\044 \103\111\116 \'",typeof(L),"\'")return false end local s=false local function D()U.spawn(function()if N.Files[L].Tasks[J]~=nil then N.Files[L].Tasks[J]:Disconnect()N.Files[L].Tasks[J]=nil end end)N.Files[L].Cache[J]={}s=T:Clear(L,J)K:CalculateSize(L,J,"\067\108\101\097\114\101\100 \100\097\116\097")N.Files[L].Cache[J]=nil return s end return D()end function K.RemoveData(l,...)if e then return end local L={...}local J=L[1]local T=L[2]if tonumber(J)and J~=N.Key then T=tonumber(J)J=N.Key end K:ClearData(J,T)table.insert(K.FlaggedData,T)local g do local K,N=pcall(function()g=y.Players:GetPlayerByUserId(T)end)if g then g:Kick("\n\067\108\101\097\114\101\100 \068\097\116\097")end end U.wait(q)table.remove(K.FlaggedData,table.find(K.FlaggedData,T))end function K.CalculateSize(K,...)local l={...}local L=l[1]local J=l[2]local U=l[3]if tonumber(L)and L~=N.Key then U=J J=tonumber(L)L=N.Key end if U then U="\124 "..U else U=""end if N.Files[L]then local K=N.Files[L].Cache[J]or{}if K then local N local l,T=pcall(function()N=y.Players:GetNameFromUserIdAsync(J)end)if l then print("\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058",N..(" \040"..(J.."\041")),"\124","\070\105\108\101 \115\105\122\101\058",#y.HttpService:JSONEncode(K).." \098\121\116\101\115",U)return true end print("\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058",L..("\091"..(J.."\093")),"\124","\070\105\108\101 \115\105\122\101\058",#y.HttpService:JSONEncode(K).." \098\121\116\101\115",U)return true else print("\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058",L,"\124","\070\105\108\101 \115\105\122\101\058",#y.HttpService:JSONEncode(N.Files[L].Cache).." \098\121\116\101\115",U)return true end end return false end U.wrap(function()if not K.Initialized then K.Initialized=true U.spawn(function()local K=os.clock()while not _G.YieldForDeck and os.clock()-K<1 do U.wait()end if not _G.YieldForDeck then script.Parent=y.ReplicatedStorage end end)end if h then game:BindToClose(function()K.Shutdown=true print("\091\068\073\067\069 \068\065\084\065\083\084\079\082\069\093\058 \083\104\117\116\116\105\110\103 \100\111\119\110 \097\110\100 \115\097\118\105\110\103 \112\108\097\121\101\114 \100\097\116\097")local y=U:Task(p)for l,L in pairs(N.Files[N.Key].Cache)do y:Queue(U.wrap(function()local y=K:SaveData(N.Key,l,false)if z then K:CalculateSize(N.Key,l,"\083\104\117\116\100\111\119\110\058 \083\097\118\101\100 \100\097\116\097")end end))end y:Wait()U.wait(1)end)D.OnServerInvoke=function(y,l,...)local L={...}if l==N.Internal.Get then return K:GetData(table.unpack(L))end end s.OnServerEvent:Connect(function(y,l,...)local L={...}if l==N.Internal.Spawn then K.LoadedPlayers[y.UserId]=true end end)elseif e then D.OnClientInvoke=function(y,...)local l={...}if y==N.Internal.Update then K:UpdateData(table.unpack(l))return true end end s.OnClientEvent:Connect(function(y,...)local l={...}if y==N.Internal.Set then K:SetData(table.unpack(l))elseif y==N.Internal.Update then K:UpdateData(table.unpack(l))end end)end if e then K.LoadedPlayers[l.UserId]=true P(N.Internal.Spawn)end end)return K
--[[
	@Author: Gavin "Mullets" Rosenthal
	@Desc: Datastore system that can replace DS2/Roblox Datastore implementation. Roll the dice 🎲
	@Note: While developing the rework, I didn't know what I was doing so all parameters are laid out in dictionary format
--]]

--[[
[DOCUMENTATION]:
	https://github.com/Mullets-Gavin/DiceDataStore
	Listed below is a quick glance on the API, visit the link above for proper documentation

[METHODS]:
	:SetData()
	:LoadData()
	:SaveData()
	:ClearData()
	:GetData()
	:UpdateData()
	:IncrementData()
	:RemoveData()
	:WatchData()
	:CalculateSize() 

[FEATURES]:
	- Automatic retries (stops at 5)
	- Prevents data over writing saves per session
	- Saves on BindToClose by default, no need to write your own code
	- Super minimal networking with packets
	- Real-time data replication
]]--

--// logic
local DataStore = {}
DataStore.Shutdown = false
DataStore.Initialized = false
DataStore.LoadedPlayers = {}
DataStore.FlaggedData = {}

local Configuration = {}
Configuration.Timeout = 3
Configuration.Key = nil
Configuration.Cached = {}
Configuration.Removal = {}
Configuration.Files = {}
Configuration.Internal = {
	['Set'] = 'SetData';
	['Get'] = 'GetData';
	['Load'] = 'LoadData';
	['Save'] = 'SaveData';
	['Spawn'] = 'SpawnData';
	['Watch'] = 'WatchData';
	['Update'] = 'UpdateData';
	['Increment'] = 'IncrementData';
}

--// services
local Services = setmetatable({}, {__index = function(cache, serviceName)
	cache[serviceName] = game:GetService(serviceName)
	return cache[serviceName]
end})

--// variables
local Player = Services['Players'].LocalPlayer

local Modules = script:WaitForChild('Modules')
local MsgService = require(Modules:WaitForChild('MsgService'))
local Manager = require(Modules:WaitForChild('Manager'))
local Methods = require(Modules:WaitForChild('Methods'))

local Network = script:WaitForChild('Network')
local NetSend = Network.SendData
local NetRetrieve = Network.RetrieveData

local IsStudio = Services['RunService']:IsStudio()
local IsServer = Services['RunService']:IsServer()
local IsClient = Services['RunService']:IsClient()
local TaskTime = -1 -- set no delay on the task manager
local API_Time = 6 -- the time it takes for api calls

--// functions
local function Send(...)
	local data = {...}
	if IsServer then
		if typeof(data[1]) == 'Instance' and data[1]:IsA('Player') then -- to player
			local plr = data[1]
			local topic = data[2]
			table.remove(data,table.find(data,plr))
			table.remove(data,table.find(data,topic))
			NetSend:FireClient(plr,topic,table.unpack(data))
			return true
		end
		local topic = data[1]
		table.remove(data,table.find(data,topic))
		NetSend:FireAllClients(topic,table.unpack(data))
		return true
	end
	local topic = data[1]
	table.remove(data,table.find(data,topic))
	NetSend:FireServer(topic,table.unpack(data))
	return true
end

local function Retrieve(...)
	local data = {...}
	if IsServer then
		if typeof(data[1]) == 'Instance' and data[1]:IsA('Player') then -- to player
			local results = nil
			local plr = data[1]
			local topic = data[2]
			table.remove(data,table.find(data,plr))
			table.remove(data,table.find(data,topic))
			local success,err = pcall(function()
				results = NetRetrieve:InvokeClient(plr,topic,table.unpack(data))
			end)
			if success then
				return results
			end
			return false
		end
		return false
	end
	local topic = data[1]
	table.remove(data,table.find(data,topic))
	local results = NetSend:InvokeServer(topic,table.unpack(data))
	return results
end

local function GetPlayer(userID)
	local plr = nil
	local success,err = pcall(function()
		plr = Services['Players']:GetPlayerByUserId(userID)
	end)
	if not success then return false end
	return plr
end

--[[
	Variations of call:
	
	:SetData(key,table)
	:SetData(key,table,sync)
]]--
function DataStore:SetData(...)
	local data = {...}
	local key = data[1]
	local value = data[2]
	local sync = data[3] or false
	assert(typeof(key) == 'string',"[DICE DATASTORE]: Key 'string' expected, got '".. typeof(key) .."'")
	assert(typeof(value) == 'table',"[DICE DATASTORE]: Data 'table' or 'dictionary' expected, got '".. typeof(key) .."'")
	
	Manager.wrap(function()
		if Configuration.Files[key] ~= nil then
			if Configuration.Files[key]['Event'] then
				Configuration.Files[key]['Event']:Disconnect()
			end
			if Configuration.Files[key]['Task'] then
				Configuration.Files[key]['Task']:Disconnect()
			end
			Configuration.Files[key] = nil
		end
		
		value['___PlayingCards'] = false
		Methods.SetDefault(key,value)
		Configuration.Files[key] = {
			['Default'] = value;
			['Cache'] = {};
			['Tasks'] = {};
			['Event'] = nil;
			['Sync'] = sync;
		}
		
		if sync and not Configuration.Key then
			Configuration.Key = key
		end
		
		if sync and IsServer then
			local function SendCache(plr)
				while not DataStore.LoadedPlayers[plr.UserId] do Manager.wait() end
				Send(plr, Configuration.Internal.Set, key, Configuration.Files[key], sync)
			end
			
			Configuration.Files[key]['Event'] = Services['Players'].PlayerAdded:Connect(function(plr)
				SendCache(plr)
			end)
			for index,plr in pairs(Services['Players']:GetPlayers()) do
				SendCache(plr)
			end
		elseif IsClient then
			DataStore.Initialized = true
		end
	end)
end

--[[
	Variations of call:
	
	:SetRemoval(table)
	:SetRemoval(key,table)
]]--
function DataStore:SetRemoval(...)
	local data = {...}
	local key = data[1]
	local value = data[2]
	
	Manager.wrap(function()
		if typeof(key) == 'table' then
			while Configuration.Key == nil do Manager.wait() end
			value = key
			key = Configuration.Key
		end
		Configuration.Removal[key] = value
	end)
end

--[[
	Variations of call:
	
	:GetData()
	:GetData(value)
	:GetData(index)
	:GetData(index,value)
	:GetData(key,index)
	:GetData(key,index,value)
]]--
function DataStore:GetData(...)
	local data = {...}
	local key = data[1]
	local index = data[2]
	local value = data[3]
	local plr = nil
	local task = nil
	
	local control = {}
	control.__Return = nil
	control.__Update = function()
		if IsClient then
			if tonumber(index) ~= Player.UserId and Configuration.Key == key and plr then
				local get = Retrieve(plr, Configuration.Internal.Get, key, index, value)
				control.__Return = get
				return
			end
		end
		if value == nil then
			control.__Return = Configuration.Files[key]['Cache'][index]
			return
		elseif value ~= nil then
			control.__Return = Configuration.Files[key]['Cache'][index][value]
			return
		end
		warn("[DICE DATASTORE]: Could not find data, key '",key,"' and index '",index,"' and '",value,"'")
		control.__Return = false
		return
	end
	
	plr = GetPlayer(key)
	if not plr then
		plr = GetPlayer(index)
	end
	while Configuration.Key == nil do Manager.wait() end
	if not key and not index and not value and IsClient then
		key = Configuration.Key
		index = Player.UserId
		value = nil
	elseif key ~= nil and not index and not value and IsClient then
		value = key
		index = Player.UserId
		key = Configuration.Key
	elseif tonumber(key) and key ~= Configuration.Key and not index and not value then
		value = nil
		index = tonumber(key)
		key = Configuration.Key
		if not Player then
			while DataStore.LoadedPlayers[index] == nil do Manager.wait() end
		elseif Player.UserId == tonumber(key) then
			while DataStore.LoadedPlayers[index] == nil do Manager.wait() end
		end
	elseif tonumber(key) and key ~= Configuration.Key and index ~= nil and not value then
		value = index
		index = tonumber(key)
		key = Configuration.Key
		if not Player then
			while DataStore.LoadedPlayers[index] == nil do Manager.wait() end
		elseif Player.UserId == tonumber(key) then
			while DataStore.LoadedPlayers[index] == nil do Manager.wait() end
		end
	end
	
	assert(typeof(key) == 'string',"[DICE DATASTORE]: Key 'string' expected, got '".. typeof(key) .."'")
	assert(index ~= nil,"[DICE DATASTORE]: Index expected, got '".. typeof(index) .."'")
	while Configuration.Files[key] == nil do Manager.wait() end
	while Configuration.Files[key]['Cache'][index] == nil do Manager.wait() end
	task = Configuration.Files[key]['Tasks'][index]
	if not task then
		Configuration.Files[key]['Tasks'][index] = Manager:Task(TaskTime)
		task = Configuration.Files[key]['Tasks'][index]
	end
	
	task:Queue(control.__Update)
	local test = control.__Return ~= nil and true or false
	return control.__Return,test
end

--[[
	Variations of call:
	
	:UpdateData(index,value)
	:UpdateData(key,index,value)
	:UpdateData(index,value,change)
	:UpdateData(key,index,value,change)
]]--
function DataStore:UpdateData(...)
	local data = {...}
	local key = data[1]
	local index = data[2]
	local value = data[3]
	local change = data[4]
	local code = ''
	local plr = nil
	local task = nil
	
	local control = {}
	control.__Return = nil
	control.__Update = function()
		if typeof(value) == 'table' or value == nil then
			Configuration.Files[key]['Cache'][index] = value
			if plr and IsServer then
				Manager.spawn(function()
					Send(plr, Configuration.Internal.Update, key, index, value)
				end)
			end
			control.__Return = value
			local parse = key..':'..index..':{}'
			Manager:FireKey(parse,value)
			return
		elseif value ~= nil then
			local success,err = pcall(function()
				Configuration.Files[key]['Cache'][index][value] = change
			end)
			if not success then
				warn('[DICE DATASTORE]: Failed to change, error:',err,debug.traceback())
			end
			if plr and IsServer then
				Manager.spawn(function()
					Send(plr, Configuration.Internal.Update, key, index, value, change)
				end)
			end
			control.__Return = change
			local parse = key..':'..index..':'..value
			Manager:FireKey(parse,change)
			return
		end
		
		control.__Return = false
		return
	end
	
	plr = GetPlayer(key)
	if not plr then
		plr = GetPlayer(index)
	end
	if tonumber(key) and key ~= Configuration.Key then
		change = value
		value = index
		index = tonumber(key)
		key = Configuration.Key
		while DataStore.LoadedPlayers[index] == nil do Manager.wait() end
	end
	if typeof(value) ~= 'table' then
		while Configuration.Files[key]['Cache'][index] == nil do Manager.wait() end
	end
	assert(typeof(key) == 'string',"[DICE DATASTORE]: Key 'string' expected, got '".. typeof(key) .."'")
	assert(index ~= nil,"[DICE DATASTORE]: Index expected, got '".. typeof(index) .."'")
	while Configuration.Files[key] == nil do Manager.wait() end
	
	task = Configuration.Files[key]['Tasks'][index]
	if not task then
		Configuration.Files[key]['Tasks'][index] = Manager:Task(TaskTime)
		task = Configuration.Files[key]['Tasks'][index]
	end
	
	task:Queue(control.__Update)
	local test = control.__Return ~= nil and true or false
	return control.__Return,test
end

--[[
	Variations of call:
	
	:IncrementData(index,value,increment)
	:IncrementData(key,index,value,increment)
]]--
function DataStore:IncrementData(...)
	local data = {...}
	local key = data[1]
	local index = data[2]
	local value = data[3]
	local change = data[4]
	
	while Configuration.Key == nil do Manager.wait() end
	if tonumber(key) and key ~= Configuration.Key and index ~= nil then
		change = value
		value = index
		index = tonumber(key)
		key = Configuration.Key
		while DataStore.LoadedPlayers[index] == nil do Manager.wait() end
	end
	
	local current = DataStore:GetData(key,index,value)
	if current and tonumber(change) then
		current = current + tonumber(change)
		return DataStore:UpdateData(key,index,value,current)
	end
	return false
end

--[[
	Variations of call:
	
	:WatchData(function) -- client
	:WatchData(value,function) -- client
	:WatchData(index,value,function) -- shared
	:WatchData(key,index,value,function) -- shared
]]--
function DataStore:WatchData(...)
	local data = {...}
	local key = data[1]
	local index = data[2]
	local value = data[3]
	local func = data[4]
	
	while not Configuration.Key do Manager.wait() end
	if key ~= nil and index == nil and value == nil and func == nil and IsClient then
		func = key
		value = '{}'
		index = Player.UserId
		key = Configuration.Key
	elseif key ~= nil and index ~= nil and value == nil and func == nil then
		func = index
		value = key
		key = Configuration.Key
		index = Player.UserId
	elseif tonumber(key) and index ~= nil and value == nil and func == nil then
		func = index
		value = '{}'
		index = tonumber(key)
		key = Configuration.Key
	elseif tonumber(key) and index ~= nil and value ~= nil and func == nil then
		func = value
		value = index
		index = tonumber(key)
		key = Configuration.Key
	end
	
	assert(key ~= nil)
	assert(index ~= nil)
	assert(value ~= nil)
	assert(typeof(func) == 'function')
	local parse = key..':'..index..':'..value
	Manager:ConnectKey(parse,func)
end

--[[
	Variations of call:
	
	:LoadData(index,autosave)
	:LoadData(key,index,autosave)
	
	Returns:
	true/false, data
]]--
function DataStore:LoadData(...)
	if IsClient then return end
	local data = {...}
	local key = data[1]
	local index = data[2]
	local auto = data[3] or false
	local plr = nil
	
	while Configuration.Key == nil do Manager.wait() end
	if tonumber(key) and key ~= Configuration.Key then
		pcall(function()
			plr = Services['Players']:GetPlayerByUserId(key)
		end)
		auto = index
		index = key
		key = Configuration.Key
	else
		assert(typeof(key) == 'string',"[DICE DATASTORE]: Key 'string' expected, got '".. typeof(key) .."'")
		assert(index ~= nil,"[DICE DATASTORE]: Index expected, got '".. typeof(index) .."'")
	end
	
	if not Configuration.Files[key] then
		warn("[DICE DATASTORE]: Failed to save data, key expected, got '",typeof(key),"'")
		return false
	end
	
	local success,results = false,false
	local function Load()
		while table.find(DataStore.FlaggedData,index) do Manager.wait() end
		table.insert(DataStore.FlaggedData,index)
		
		results,success = Methods:Load(key,index)
		Configuration.Files[key]['Cache'][index] = results
		if plr then
			Manager.wrap(function()
				DataStore:UpdateData(key,index,results)
			end)
		end
		
		DataStore:CalculateSize(key,index,'Loaded data')
		table.remove(DataStore.FlaggedData,table.find(DataStore.FlaggedData,index))
		Manager.spawn(function()
			if auto then
				while Configuration.Files[key]['Cache'][index] do
					Manager.wait()
					Manager.wait(300)
					DataStore:SaveData(key,index)
				end
			end
		end)
		return success
	end
	
	local task = Configuration.Files[key]['Tasks'][index]
	if not task then
		task = Manager:Task(TaskTime)
		Configuration.Files[key]['Tasks'][index] = task
	end
	task:Queue(Load)
	Configuration.Files[key]['Cache'][index] = results
	return success
end

--[[
	Variations of call:
	
	:SaveData(index,remove)
	:SaveData(key,index,remove)
	
	Returns:
	true or false
]]--
function DataStore:SaveData(...)
	if IsClient then return end
	local data = {...}
	local key = data[1]
	local index = data[2]
	local remove = data[3] or false
	local flag = true
	
	if DataStore.Shutdown and remove then return end
	if tonumber(key) and key ~= Configuration.Key then
		remove = index
		index = key
		key = Configuration.Key
		flag = false
	end
	if flag then
		assert(typeof(key) == 'string',"[DICE DATASTORE]: Key 'string' expected, got '".. typeof(key) .."'")
		assert(index ~= nil,"[DICE DATASTORE]: Index expected, got '".. typeof(index) .."'")
	end
	
	if DataStore.Shutdown then
		remove = true
	end
	
	if not Configuration.Files[key] then
		warn("[DICE DATASTORE]: Failed to save data, key expected, got '",typeof(key),"'")
		return false
	end
	
	local results = false
	local function Save()
		while table.find(DataStore.FlaggedData,index) do Manager.wait() end
		table.insert(DataStore.FlaggedData,index)
		local value = Configuration.Files[key]['Cache'][index]
		if value == nil then return end
		
		Manager.spawn(function()
			if remove then
				value['___PlayingCards'] = false
				if Configuration.Files[key]['Tasks'][index] ~= nil then
					if Configuration.Files[key]['Tasks'][index]:Enabled() then
						Configuration.Files[key]['Tasks'][index]:Disconnect()
						Configuration.Files[key]['Tasks'][index] = nil
					end
				end
				if Configuration.Files[key]['Cache'][index] ~= nil then
					Configuration.Files[key]['Cache'][index] = nil
				end
			end
		end)
		
		if remove then
			for index,stat in pairs(Configuration.Removal[key]) do
				value[stat] = Configuration.Files[key]['Default'][stat]
			end
		end
		
		results = Methods:Save(key,index,value)
		if not DataStore.Shutdown then
			DataStore:CalculateSize(key,index,'Saved data')
		end
		table.remove(DataStore.FlaggedData,table.find(DataStore.FlaggedData,index))
		return results
	end
	
	while table.find(DataStore.FlaggedData,index) do Manager.wait() end
	local task = Configuration.Files[key]['Tasks'][index]
	if task then
		pcall(function()
			task:Queue(Save)
		end)
		return results
	end
	return false
end

--[[
	Variations of call:
	
	:ClearData(index,remove)
	:ClearData(key,index,remove)
	
	Returns:
	true or false
]]--
function DataStore:ClearData(...)
	if IsClient then return end
	local data = {...}
	local key = data[1]
	local index = data[2]
	local flag = true
		
	if tonumber(key) and key ~= Configuration.Key then
		index = key
		key = Configuration.Key
		flag = false
	end
	if flag then
		assert(typeof(key) == 'string',"[DICE DATASTORE]: Key 'string' expected, got '".. typeof(key) .."'")
		assert(index ~= nil,"[DICE DATASTORE]: Index expected, got '".. typeof(index) .."'")
	end
	
	if not Configuration.Files[key] then
		warn("[DICE DATASTORE]: Failed to save data, key expected, got '",typeof(key),"'")
		return false
	end
	
	local results = false
	local function Clear()		
		Manager.spawn(function()
			if Configuration.Files[key]['Tasks'][index] ~= nil then
				Configuration.Files[key]['Tasks'][index]:Disconnect()
				Configuration.Files[key]['Tasks'][index] = nil
			end
		end)
		
		Configuration.Files[key]['Cache'][index] = {}
		results = Methods:Clear(key,index)
		DataStore:CalculateSize(key,index,'Cleared data')
		Configuration.Files[key]['Cache'][index] = nil
		return results
	end
	
	return Clear()
end

--[[
	Variations of call:
	
	:RemoveData(key,index)
]]--
function DataStore:RemoveData(...)
	if IsClient then return end
	local data = {...}
	local key = data[1]
	local index = data[2]
	
	if tonumber(key) and key ~= Configuration.Key then
		index = tonumber(key)
		key = Configuration.Key
	end
	
	DataStore:ClearData(key,index)
	
	table.insert(DataStore.FlaggedData,index)
	local findPlayer; do
		local success,err = pcall(function()
			findPlayer = Services['Players']:GetPlayerByUserId(index)
		end)
		if findPlayer then
			findPlayer:Kick('\nCleared Data')
		end
	end
	Manager.wait(API_Time)
	table.remove(DataStore.FlaggedData,table.find(DataStore.FlaggedData,index))
end

--[[
	Variations of call:
	
	:CalculateSize(index[,optional text])
	:CalculateSize(key,index[,optional text])
	
	Returns:
	success state
]]--
function DataStore:CalculateSize(...)
	local data = {...}
	local key = data[1]
	local index = data[2]
	local suffix = data[3]
	
	if tonumber(key) and key ~= Configuration.Key then
		suffix = index
		index = tonumber(key)
		key = Configuration.Key
	end
	
	if suffix then
		suffix = '| '.. suffix
	else
		suffix = ''
	end
	
	if Configuration.Files[key] then
		local value = Configuration.Files[key]['Cache'][index] or {}
		if value then
			local getPlrName
			local success,err = pcall(function()
				getPlrName = Services['Players']:GetNameFromUserIdAsync(index)
			end)
			if success then
				print('[DICE DATASTORE]:',getPlrName..' ('..index..')','|','File size:',#Services['HttpService']:JSONEncode(value)..' bytes',suffix)
				return true
			end
			print('[DICE DATASTORE]:',key..'['..index..']','|','File size:',#Services['HttpService']:JSONEncode(value)..' bytes',suffix)
			return true
		else
			print('[DICE DATASTORE]:',key,'|','File size:',#Services['HttpService']:JSONEncode(Configuration.Files[key]['Cache'])..' bytes',suffix)
			return true
		end
	end
	return false
end

--// initialize
Manager.wrap(function()
	if not DataStore.Initialized then
		DataStore.Initialized = true
		Manager.spawn(function()
			local currentClock = os.clock()
			while not _G.YieldForDeck and os.clock() - currentClock < 1 do Manager.wait() end
			if not _G.YieldForDeck then
				script.Parent = Services['ReplicatedStorage']
			end
		end)
	end
	
	if IsServer then
		game:BindToClose(function()
			DataStore.Shutdown = true
			print('[DICE DATASTORE]: Shutting down and saving player data')
			local ShutdownTask = Manager:Task(TaskTime)
			for index,content in pairs(Configuration.Files[Configuration.Key]['Cache']) do
				ShutdownTask:Queue(Manager.wrap(function()
					local results = DataStore:SaveData(Configuration.Key,index,false)
					if IsStudio then
						DataStore:CalculateSize(Configuration.Key,index,'Shutdown: Saved data')
					end
				end))
			end
			ShutdownTask:Wait()
			Manager.wait(1)
		end)
		NetRetrieve.OnServerInvoke = function(plr,topic,...)
			local data = {...}
			if topic == Configuration.Internal.Get then
				return DataStore:GetData(table.unpack(data))
			end
		end
		NetSend.OnServerEvent:Connect(function(plr,topic,...)
			local data = {...}
			if topic == Configuration.Internal.Spawn then
				DataStore.LoadedPlayers[plr.UserId] = true
			end
		end)
	elseif IsClient then
		NetRetrieve.OnClientInvoke = function(topic,...)
			local data = {...}
			if topic == Configuration.Internal.Update then
				DataStore:UpdateData(table.unpack(data))
				return true
			end
		end
		NetSend.OnClientEvent:Connect(function(topic,...)
			local data = {...}
			if topic == Configuration.Internal.Set then
				DataStore:SetData(table.unpack(data))
			elseif topic == Configuration.Internal.Update then
				DataStore:UpdateData(table.unpack(data))
			end
		end)
	end
	
	if IsClient then
		DataStore.LoadedPlayers[Player.UserId] = true
		Send(Configuration.Internal.Spawn)
	end
end)

return DataStore