local A=game:GetService("\068\097\116\097\083\116\111\114\101\083\101\114\118\105\099\101")local S={}local n=false local X=false if game.PrivateServerId~=""and game.PrivateServerOwnerId~=0 then X=game.PrivateServerOwnerId end local c={}local v={}local y={}local w local h=error local function W(A,...)if(game:GetService("\082\117\110\083\101\114\118\105\099\101")):IsStudio()then print(A)end return h(...)end function Module(n,h,K,...)K=K or{nocache=false,nosandbox=false}if type(K.nocache)=="\110\105\108"then K.nocache=false end if type(K.nosandbox)=="\110\105\108"then K.nosandbox=false end table.insert(h.Data.Logs.System,{os.time();"\068\097\116\097\115\116\111\114\101","\076\105\115\116\101\110\105\110\103 \116\111 \099\097\116\101\103\111\114\121 "..n})if not w then w=h end if X and not K.nosandbox then n=n..("\095\080\114\118"..X)end if c[n]then return c[n]end if not S[n]then S[n]={}end local j={}j.Store=A:GetDataStore(n)j.Name=n function j.Save(X,A,S)table.insert(h.Data.Logs.System,{os.time(),"\068\097\116\097\115\116\111\114\101",n..("\059 \083\097\118\105\110\103 \116\111 "..A)})if type(A)~="\115\116\114\105\110\103"then W(A,"\069\118\101\110 \069\097\115\105\101\114 \068\097\116\097\115\116\111\114\101 \124 \069\120\112\101\099\116\101\100 \115\116\114\105\110\103\044 \103\111\116 "..type(A))end if v[A]then v[A].Data=S return v[A]end local c={}c.Complete=false c.InProgress=false c.Attempts=0 c.NextTry=0 c.Type="\083\097\118\101"c.Key=A c.Category=X c.Cancelled=false c.Cache=not K.nocache if type(S)=="\116\097\098\108\101"then S="\095\116\098\108\058"..w.Serializer.encode(S)end c.Data=S function c.wait(A)repeat task.wait()until A.Complete or A.Cancelled return c end function c.Cancel(A)A.Cancelled=true end function c.Focus(S)local n={}n.Task=c function n.Redo(S)c.Cancelled=false c.Complete=false c.InProgress=false c.Attempts=0 c.NextTry=0 v[A]=c table.insert(y,c)return c end return n end v[A]=c table.insert(y,c)return c end function j.Update(X,A,S)table.insert(h.Data.Logs.System,{os.time();"\068\097\116\097\115\116\111\114\101",n..("\059 \085\112\100\097\116\105\110\103 "..A)})if v[A]then if v[A].Data==nil then v[A].Data={}end table.insert(v[A].Data,S)return v[A]end local c={}c.Complete=false c.InProgress=false c.Attempts=0 c.NextTry=0 c.Type="\085\112\100\097\116\101"c.Data={S}c.Key=A c.Category=X c.Cancelled=false c.Cache=not K.nocache function c.wait(A)repeat task.wait()until A.Complete or A.Cancelled return c end function c.Cancel(A)A.Cancelled=true end function c.Focus(S)local n={}n.Task=c function n.Redo(S)c.Cancelled=false c.Complete=false c.InProgress=false c.Attempts=0 c.NextTry=0 v[A]=c table.insert(y,c)return c end return n end v[A]=c table.insert(y,c)return c end function j.Load(S,A)table.insert(h.Data.Logs.System,{os.time(),"\068\097\116\097\115\116\111\114\101";n..("\059 \076\111\097\100\101\100 \102\114\111\109 "..A)})if v[A]then return v[A]end local X={}X.Complete=false X.InProgress=false X.Attempts=0 X.NextTry=0 X.Type="\076\111\097\100"X.Data=nil X.Key=A X.Category=S X.Cancelled=false X.Cache=not K.nocache function X.wait(A)repeat task.wait()until A.Complete or A.Cancelled return X end function X.Cancel(A)A.Cancelled=true end function X.Focus(S)local n={}n.Task=X function n.Redo(S)X.Cancelled=false X.Complete=false X.InProgress=false X.Attempts=0 X.NextTry=0 v[A]=X table.insert(y,X)return X end function n.Increment(S,A)if type(X.Data)=="\110\117\109\098\101\114"then X.Data=X.Data+A return X.Data else return nil end end function n.Decrement(S,A)if type(X.Data)=="\110\117\109\098\101\114"then X.Data=X.Data-A return X.Data else return nil end end return n end v[A]=X table.insert(y,X)return X end function j.Nullify(S,A)table.insert(h.Data.Logs.System,{os.time(),"\068\097\116\097\115\116\111\114\101";n..("\059 \068\101\108\101\116\101\100 "..A)})if v[A]then v[A].Data=nil return v[A]end local X={}X.Complete=false X.InProgress=false X.Attempts=0 X.NextTry=0 X.Type="\078\117\108\108\105\102\121"X.Data=nil X.Key=A X.Category=S X.Cancelled=false X.Cache=not K.nocache function X.wait(A)repeat task.wait()until A.Complete or A.Cancelled end function X.Cancel(A)A.Cancelled=true end function X.Focus(S)local n={}n.Task=X function n.Redo(S)X.Cancelled=false X.Complete=false X.InProgress=false X.Attempts=0 X.NextTry=0 v[A]=X table.insert(y,X)return X end return n end v[A]=X table.insert(y,v[A])return X end function j.Remove(S,A)table.insert(h.Data.Logs.System,{os.time(),"\068\097\116\097\115\116\111\114\101",n..("\059 \068\101\108\101\116\101\100 "..A)})if v[A]then v[A].Data=nil return v[A]end local X={}X.Complete=false X.InProgress=false X.Attempts=0 X.NextTry=0 X.Type="\078\117\108\108\105\102\121"X.Data=nil X.Key=A X.Category=S X.Cancelled=false X.Cache=not K.nocache function X.wait(A)repeat task.wait()until A.Complete or A.Cancelled end function X.Cancel(A)A.Cancelled=true end function X.Focus(S)local n={}n.Task=X function n.Redo(S)X.Cancelled=false X.Complete=false X.InProgress=false X.Attempts=0 X.NextTry=0 v[A]=X table.insert(y,X)return X end return n end v[A]=X table.insert(y,v[A])return X end c[n]=j return j end task.spawn(function()while true do task.wait(.05)for A,S in y do if A>2 and not y[A-1]then y[A-1]=S y[A]=nil end end if#y>0 then local A=y[1]if not A.Complete and not A.InProgress then repeat task.wait(.1)A.NextTry=A.NextTry-.1 until A.NextTry<0 task.spawn(function()local X=A.Data A.Data=nil A.InProgress=true local c,h if not w.Data.Settings.DisableDS then c,h=pcall(function()if A.Type=="\083\097\118\101"then if not X then return end if A.Cache then if S[A.Category.Name][A.Key]==X then warn("\069\118\101\110 \069\097\115\105\101\114 \068\097\116\097\115\116\111\114\101 \124 "..(A.Key.." - \085\110\110\101\099\101\115\115\097\114\121 \115\097\118\101 \114\101\113\117\101\115\116\033"))return end S[A.Category.Name][A.Key]=X end if not n then A.Category.Store:SetAsync(A.Key,X)end elseif A.Type=="\078\117\108\108\105\102\121"then if not n then A.Category.Store:RemoveAsync(A.Key)end S[A.Category.Name][A.Key]=nil elseif A.Type=="\085\112\100\097\116\101"then if A.Cache then if S[A.Category.Name][A.Key]==X then warn("\069\118\101\110 \069\097\115\105\101\114 \068\097\116\097\115\116\111\114\101 \124 "..(A.Key.." - \085\110\110\101\099\101\115\115\097\114\121 \117\112\100\097\116\101 \114\101\113\117\101\115\116\033"))return end S[A.Category.Name][A.Key]=X end local function c(A)for S,n in pairs(X)do A=n(A)end return A end if not n then A.Category.Store:UpdateAsync(A.Key,c)end else if A.Cache and S[A.Category.Name][A.Key]then local n=S[A.Category.Name][A.Key]if type(n)=="\115\116\114\105\110\103"and string.sub(n,1,5)=="\095\116\098\108\058"then A.Data=w.Serializer.decode(string.sub(n,6))else A.Data=n end else local S=A.Category.Store:GetAsync(A.Key)if type(S)=="\115\116\114\105\110\103"and string.sub(S,1,5)=="\095\116\098\108\058"then A.Data=w.Serializer.decode(string.sub(S,6))else A.Data=S end end end end)else c=true end if c then A.Complete=true table.remove(y,1)v[A.Key]=nil if A.Type~="\076\111\097\100"then if A.Data~=nil then A.Complete=false table.insert(y,A)v[A.Key]=A else A.Data=X end end else if A.Type~="\076\111\097\100"and(game:GetService("\082\117\110\083\101\114\118\105\099\101")):IsStudio()then warn("\068\097\116\097\083\116\111\114\101 \115\097\118\101\047\117\112\100\097\116\101 \102\097\105\108\101\100 \105\110 \097 \083\116\117\100\105\111 \115\101\115\115\105\111\110\046 \073\115 \065\080\073 \097\099\099\101\115\115 \116\111 \068\097\116\097\083\116\111\114\101\115 \101\110\097\098\108\101\100\063")end warn("\069\118\101\110 \069\097\115\105\101\114 \068\097\116\097\115\116\111\114\101 \124 \065\110 \101\114\114\111\114 \104\097\115 \111\099\099\117\114\101\100 \119\105\116\104 \116\104\101 \100\097\116\097\115\116\111\114\101\115\058 "..h)A.Attempts=A.Attempts+1 A.NextTry=math.min(math.pow(2,A.Attempts-1),5)if A.Attempts==3 then A.Cancelled=true warn("\069\118\101\110 \069\097\115\105\101\114 \068\097\116\097\115\116\111\114\101 \124 \067\097\110\099\101\108\108\101\100 \097\116\116\101\109\112\116 \116\111 "..(A.Type..(" \107\101\121 \""..(A.Key.."\" \100\117\101 \116\111 \102\097\105\108\105\110\103 \051 \116\105\109\101\115\046"))))end print("\069\118\101\110 \069\097\115\105\101\114 \068\097\116\097\115\116\111\114\101 \124 \083\097\118\105\110\103 \116\111 \107\101\121 \'"..(A.Key..("\' \102\097\105\108\101\100 \119\105\116\104 \101\114\114\111\114\058 \""..(h..("\"\044 \114\101\116\114\121\105\110\103 \105\110 "..(A.NextTry.." \115\101\099\111\110\100\115"))))))A.Data=X end A.InProgress=false end)else table.remove(y,1)v[A.Key]=nil A=nil end end end end)return Module
--[[AtAltitude's Classic Datastore Module || Forked by 0bBinary for Sezei.me's uses.
--	EEDS / Even Easier DataStore

	API spec:
	
	DataCategory = Module(name)
		Returns a DataCategory to save data to and retrieve data from. The name can be any string (such as 
		"Leaderboard" or "Player Data")
	
	Task = DataCategory:Save(key,data)
		Asks the module to save the given data under the given key. It will return a 'Task' which contains 
		information and methods for the monitoring and control of the data saving process. Sending a new
		request while the old one is in progress will simply result in the old request being overridden.
	
	Task = DataCategory:Update(key,updateFunc)
		Asks the module to update the data under the given key according to the given function. This comes with
		a failsafe to prevent the overriding of previously saved data.
	
	Task = DataCategory:Load(key)
		Asks the module to retrieve the data stored under the given key. It will return a 'Task' which contains
		information and methods for the monitoring and control of the data retrieval process.	
		
	Task = DataCategory:Nullify(key) || DataCategory:Remove(key)
		Asks the module to nullify the key by setting the stored data to nil (using RemoveAsync).
	
	Task:wait()
		Waits until the task completes or is cancelled.
	
	Task:Cancel()
		Cancels the task.
	
	Task.Complete
		Says whether or not the task has been completed.
	
	Task.Attempts
		The amount of previous, failed attempts to complete the task.
	
	Task.NextTry
		Time in seconds before the next attempt
	
	Task.Type
		"Load" if the task is to retrieve data, "Save" if the task is to save data.
	
	Task.Data
		The data to be saved, or the data that was retrieved depending on the type of task.
	
	Task.Key
		The key the task is supposed to read/write.
		
	-- META TASKS --
	
	MetaTask = Task:Focus()
		Focuses on the singular task.
		
	MetaTask:Redo()
		Queue the task to be redone.
	
	MetaTask:Increment(n)
		Load Only.
		If the task data is a number, increments the data by data+n. If n is not added, then n = 1 by default.
		If the task data is NaN, the function errors.
		
	MetaTask:Decrement(n)
		Load Only.
		If the task data is a number, decrements the data by data-n. If n is not added, then n = 1 by default.
		If the task data is NaN, the function errors.
	
	Task = MetaTask.Task
		Returns the origin Task.
--]]

--Get important instances
local dataStoreSvc 	= game:GetService("DataStoreService")
local datacache = {};

local disabled = false;
local privateserver = false;

-- Check if the game is running on a private server; Datastores will use a private version of itself, allowing VIP server sandboxing.
if (game.PrivateServerId ~= "" and game.PrivateServerOwnerId ~= 0) then -- Checks if the game is running on a private server.
	privateserver = game.PrivateServerOwnerId;
end

--Information storage
local categories 	= {}
local tasks 		= {}
local taskQueue 	= {}

--Making R:A env global after a single call
local api;

local olderror = error
local function error(debg,...)
	if game:GetService("RunService"):IsStudio() then
		print(debg)
	end

	return olderror(...)
end

--Module
function Module(name,env,Vars,...)
	Vars = Vars or {
		nocache = false; -- Disable Cache
		nosandbox = false; -- Disable sandboxing; Init only
	};
	
	if type(Vars.nocache)=='nil' then Vars.nocache = false end;
	if type(Vars.nosandbox)== 'nil' then Vars.nosandbox = false end;
	
	table.insert(env.Data.Logs.System, {os.time(), "Datastore", "Listening to category "..name});
	if not api then api = env end;

	if privateserver and not Vars.nosandbox then -- Sandbox the datastore to the private server's owner ID.
		name = name.."_Prv"..privateserver;
	end

	--Check if we've already done all the hard work, and if yes, just give them what we already have
	if (categories[name]) then return categories[name] end

	--Oh no, it looks like we haven't dealt with this yet
	if not datacache[name] then
		datacache[name] = {};
	end

	local DataCategory = {}
	DataCategory.Store = dataStoreSvc:GetDataStore(name)
	DataCategory.Name = name
	function DataCategory:Save(key,data)
		table.insert(env.Data.Logs.System, {os.time(), "Datastore", name.."; Saving to "..key});
		if type(key) ~= "string" then error(key,"Even Easier Datastore | Expected string, got "..type(key)) end
		--Overwrite old data so we don't waste precious requests
		if (tasks[key]) then tasks[key].Data = data return tasks[key] end

		local Task = {}
		Task.Complete = false
		Task.InProgress = false
		Task.Attempts = 0
		Task.NextTry = 0
		Task.Type = "Save"
		Task.Key = key
		Task.Category = self
		Task.Cancelled = false
		
		Task.Cache = not Vars.nocache;

		if type(data) == "table" then
			data = "_tbl:"..api.Serializer.encode(data);
		end

		Task.Data = data

		function Task:wait()
			repeat
				task.wait()
			until (self.Complete or self.Cancelled)
			return Task
		end
		function Task:Cancel() 
			self.Cancelled = true;
		end
		function Task:Focus()
			local MetaTask = {}

			MetaTask.Task = Task;

			function MetaTask:Redo()
				Task.Cancelled = false;
				Task.Complete = false;
				Task.InProgress = false;
				Task.Attempts = 0
				Task.NextTry = 0
				tasks[key] = Task --Store it so we don't do overlap work
				table.insert(taskQueue,Task) --Store it under a numerical value as well
				return Task
			end

			return MetaTask
		end

		tasks[key] = Task --Store it so we don't do overlap work
		table.insert(taskQueue,Task) --Store it under a numerical value as well
		return Task
	end

	function DataCategory:Update(key,updateFunc)
		table.insert(env.Data.Logs.System, {os.time(), "Datastore", name.."; Updating "..key});
		--Overwrite old data so we don't waste precious requests
		if (tasks[key]) then 
			if (tasks[key].Data == nil) then
				tasks[key].Data = {}
			end
			table.insert(tasks[key].Data,updateFunc)
			return tasks[key] 
		end

		local Task = {}
		Task.Complete = false
		Task.InProgress = false
		Task.Attempts = 0
		Task.NextTry = 0
		Task.Type = "Update"
		Task.Data = {updateFunc}
		Task.Key = key
		Task.Category = self
		Task.Cancelled = false
		
		Task.Cache = not Vars.nocache;

		function Task:wait() 
			repeat 
				task.wait() 
			until (self.Complete or self.Cancelled) 
			return Task
		end
		function Task:Cancel() 
			self.Cancelled = true 
		end
		function Task:Focus()
			local MetaTask = {}

			MetaTask.Task = Task;

			function MetaTask:Redo()
				Task.Cancelled = false;
				Task.Complete = false;
				Task.InProgress = false;
				Task.Attempts = 0
				Task.NextTry = 0
				tasks[key] = Task --Store it so we don't do overlap work
				table.insert(taskQueue,Task) --Store it under a numerical value as well
				return Task
			end

			return MetaTask
		end

		tasks[key] = Task --Store it so we don't do overlap work
		table.insert(taskQueue,Task) --Store it under a numerical value as well
		return Task
	end

	function DataCategory:Load(key)
		table.insert(env.Data.Logs.System, {os.time(), "Datastore", name.."; Loaded from "..key});
		if (tasks[key]) then return tasks[key] end

		local Task = {}
		Task.Complete = false
		Task.InProgress = false
		Task.Attempts = 0
		Task.NextTry = 0
		Task.Type = "Load"
		Task.Data = nil
		Task.Key = key
		Task.Category = self
		Task.Cancelled = false
		
		Task.Cache = not Vars.nocache;

		function Task:wait() 
			repeat 
				task.wait();
			until (self.Complete or self.Cancelled) 
			return Task
		end
		function Task:Cancel() 
			self.Cancelled = true 
		end
		function Task:Focus()
			local MetaTask = {}

			MetaTask.Task = Task;

			function MetaTask:Redo()
				Task.Cancelled = false;
				Task.Complete = false;
				Task.InProgress = false;
				Task.Attempts = 0
				Task.NextTry = 0
				tasks[key] = Task --Store it so we don't do overlap work
				table.insert(taskQueue,Task) --Store it under a numerical value as well
				return Task
			end

			function MetaTask:Increment(n)
				if type(Task.Data) == 'number' then
					Task.Data = Task.Data+n
					return Task.Data;
				else
					return nil;
				end
			end

			function MetaTask:Decrement(n)
				if type(Task.Data) == 'number' then
					Task.Data = Task.Data-n
					return Task.Data;
				else
					return nil;
				end
			end

			return MetaTask
		end

		tasks[key] = Task --Store it so we don't do overlap work
		table.insert(taskQueue,Task) --Store it under a numerical value as well
		return Task
	end

	function DataCategory:Nullify(key)
		table.insert(env.Data.Logs.System, {os.time(), "Datastore", name.."; Deleted "..key});
		--Overwrite old data so we don't waste precious requests
		if (tasks[key]) then tasks[key].Data = nil return tasks[key] end

		local Task = {}
		Task.Complete = false
		Task.InProgress = false
		Task.Attempts = 0
		Task.NextTry = 0
		Task.Type = "Nullify"
		Task.Data = nil
		Task.Key = key
		Task.Category = self
		Task.Cancelled = false
		
		Task.Cache = not Vars.nocache;

		function Task:wait() repeat task.wait() until (self.Complete or self.Cancelled) end
		function Task:Cancel() self.Cancelled = true; end
		function Task:Focus()
			local MetaTask = {}

			MetaTask.Task = Task;

			function MetaTask:Redo()
				Task.Cancelled = false;
				Task.Complete = false;
				Task.InProgress = false;
				Task.Attempts = 0
				Task.NextTry = 0
				tasks[key] = Task --Store it so we don't do overlap work
				table.insert(taskQueue,Task) --Store it under a numerical value as well
				return Task
			end

			return MetaTask
		end

		tasks[key] = Task --Store it so we don't do overlap work
		table.insert(taskQueue, tasks[key]) --Store it under a numerical value as well
		return Task
	end

	function DataCategory:Remove(key) -- Alias for Nullify
		table.insert(env.Data.Logs.System, {os.time(), "Datastore", name.."; Deleted "..key});
		--Overwrite old data so we don't waste precious requests
		if (tasks[key]) then tasks[key].Data = nil return tasks[key] end

		local Task = {}
		Task.Complete = false
		Task.InProgress = false
		Task.Attempts = 0
		Task.NextTry = 0
		Task.Type = "Nullify"
		Task.Data = nil
		Task.Key = key
		Task.Category = self
		Task.Cancelled = false
		
		Task.Cache = not Vars.nocache;

		function Task:wait() repeat task.wait() until (self.Complete or self.Cancelled) end
		function Task:Cancel() self.Cancelled = true; end
		function Task:Focus()
			local MetaTask = {}

			MetaTask.Task = Task;

			function MetaTask:Redo()
				Task.Cancelled = false;
				Task.Complete = false;
				Task.InProgress = false;
				Task.Attempts = 0
				Task.NextTry = 0
				tasks[key] = Task --Store it so we don't do overlap work
				table.insert(taskQueue,Task) --Store it under a numerical value as well
				return Task
			end

			return MetaTask
		end

		tasks[key] = Task --Store it so we don't do overlap work
		table.insert(taskQueue, tasks[key]) --Store it under a numerical value as well
		return Task
	end

	categories[name] = DataCategory --Make sure we don't have to do all that again
	return DataCategory --Tell them that we're done and give them the result
end

--Creating new threads is usually bad practice, but in our case we want the module to continuously read
--the information we give it and attempt to do what we want it to.
task.spawn(function()
	while true do
		task.wait(0.05)
		for k, tsk in taskQueue do -- remove all redundant key takers (since apparently that holds up the queue???)
			if k>2 and (not taskQueue[k-1]) then
				taskQueue[k-1] = tsk;
				taskQueue[k] = nil; -- clear up its own slot so others can take the spot
			end
		end
		if (#taskQueue > 0) then
			local Task = taskQueue[1]

			if (not Task.Complete and not Task.InProgress) then
				--Wait until we're supposed to execute the task (and yes, this blocks all following tasks too)
				--No point in making more requests when one is failing.
				--[[print("Now handling task of type " 
					.. Task.Type .. " on key " 
					.. Task.Key .. "; Next try in " 
					.. Task.NextTry .. " seconds.")]]

				repeat
					task.wait(0.1)
					Task.NextTry = Task.NextTry - 0.1
				until Task.NextTry < 0

				--print("Wait time complete, attempting to execute task...")

				--Saving, updating and loading unfortunately yields, so we're creating another thread here.
				task.spawn(function()
					--Store the information elsewhere so we can tell if there have been more requests to save to
					--the same key
					local data = Task.Data
					Task.Data = nil

					--Make sure we don't do overlap work (debounce)
					Task.InProgress = true

					--Attempt to save/load the information and see if it was successful
					local ok,err;
					if not api.Data.Settings.DisableDS then
						ok,err = pcall(function()
							if (Task.Type == "Save") then
								if not data then return end
								if Task.Cache then
									if datacache[Task.Category.Name][Task.Key] == data then warn("Even Easier Datastore | " .. Task.Key.." - Unnecessary save request!"); return end;
									datacache[Task.Category.Name][Task.Key] = data;
								end
								if not disabled then
									Task.Category.Store:SetAsync(Task.Key,data) -- Save in here too just in-case the API will go down. - It'll destroy the Sync tho.
								end
							elseif (Task.Type == "Nullify") then
								if not disabled then
									Task.Category.Store:RemoveAsync(Task.Key)
								end

								datacache[Task.Category.Name][Task.Key] = nil;
							elseif (Task.Type == "Update") then
								--Rather than making tons of requests to the datastore, we cumulate the modifier functions here
								--and only request with the result
								if Task.Cache then
									if datacache[Task.Category.Name][Task.Key] == data then warn("Even Easier Datastore | " .. Task.Key.." - Unnecessary update request!"); return end;
									datacache[Task.Category.Name][Task.Key] = data;
								end
								local function func(variant)
									for _,f in pairs(data) do variant = f(variant) end
									return variant
								end
								if not disabled then
									Task.Category.Store:UpdateAsync(Task.Key,func)
								end
							else
								if Task.Cache and datacache[Task.Category.Name][Task.Key] then
									local dat = datacache[Task.Category.Name][Task.Key];
									if type(dat) == "string" and string.sub(dat,1,5) == "_tbl:" then
										Task.Data = api.Serializer.decode(string.sub(dat,6));
									else
										Task.Data = dat
									end
								else
									local dat = Task.Category.Store:GetAsync(Task.Key)
									if type(dat) == "string" and string.sub(dat,1,5) == "_tbl:" then
										Task.Data = api.Serializer.decode(string.sub(dat,6));
									else
										Task.Data = dat
									end
								end
							end
						end)
					else
						ok = true;
					end

					if (ok) then
						--If it was a success, we can call it done and remove it from the queue.
						Task.Complete = true
						table.remove(taskQueue,1)
						tasks[Task.Key] = nil

						--Oh, but wait! If we tried pushing more data while the datastore was busy, we may want to
						--redo this so we have the latest info.
						if (Task.Type ~= "Load") then
							if (Task.Data ~= nil) then
								--Yeah, can't quite exit yet, sorry. But we'll give you an update so you don't feel left alone!
								Task.Complete = false
								--print("Saving to key '" .. Task.Key .. "' succeeded but will be repeated")

								--Not quite done yet, but add it to the end of the queue
								table.insert(taskQueue,Task)
								tasks[Task.Key] = Task
							else
								--We want this to be the original value 
								Task.Data = data
							end
						end
						--[[print("Task of type "  -- debug
							.. Task.Type .. " on key " 
							.. Task.Key .. " succeeded.")]]
					else
						--Check if we're running on Studio and attempting to update/save; if so, warn user that this may be the reason we failed
						if (Task.Type ~= "Load" and game:GetService("RunService"):IsStudio()) then
							warn("DataStore save/update failed in a Studio session. Is API access to DataStores enabled?")
						end
						warn("Even Easier Datastore | An error has occured with the datastores: "..err)

						--Looks like it failed. We'll take a note of that and retry again later.
						Task.Attempts = Task.Attempts + 1
						Task.NextTry = math.min(math.pow(2,Task.Attempts-1),5)

						if Task.Attempts == 3 then -- Auto-cancel after 3 attempts; Notify the user.
							Task.Cancelled = true
							warn("Even Easier Datastore | Cancelled attempt to "..Task.Type.." key \""..Task.Key.."\" due to failing 3 times.");
						end

						--Also, we'll tell you so you know what's happening.
						print("Even Easier Datastore | Saving to key '" .. Task.Key .. "' failed with error: \"" .. err .. "\", retrying in " .. Task.NextTry .. " seconds")
						Task.Data = data
					end

					--Debounce again
					Task.InProgress = false
				end)
			else
				--If the task was cancelled, get rid of it.
				table.remove(taskQueue,1)
				tasks[Task.Key] = nil
				Task = nil; -- destroy the task
			end
		end
	end
end)

return Module
