local v={}local m={}local u={}local q=game:GetService("\068\097\116\097\083\116\111\114\101\083\101\114\118\105\099\101")local d={}if(game:GetService("\080\108\097\121\101\114\115")).LocalPlayer then warn("\077\111\099\107\101\100 \068\097\116\097\083\116\111\114\101\083\101\114\118\105\099\101 \105\115 \102\117\110\099\116\105\111\110\105\110\103 \111\110 \116\104\101 \099\108\105\101\110\116\058 \084\104\101 \114\101\097\108 \068\097\116\097\083\116\111\114\101\083\101\114\118\105\099\101 \119\105\108\108 \110\111\116 \119\111\114\107 \111\110 \116\104\101 \099\108\105\101\110\116")end function m.GetDataStore(u,v,m)assert(type(v)=="\115\116\114\105\110\103","\068\097\116\097\083\116\111\114\101 \110\097\109\101 \109\117\115\116 \098\101 \097 \115\116\114\105\110\103\059 \103\111\116"..type(v))assert(type(m)=="\115\116\114\105\110\103"or m==nil,"\068\097\116\097\083\116\111\114\101 \115\099\111\112\101 \109\117\115\116 \098\101 \097 \115\116\114\105\110\103\059 \103\111\116"..type(m))m=m or"\103\108\111\098\097\108"if d[m]and d[m][v]then return d[m][v]end local q={}local X={}local T={}function X.SetAsync(u,v,m)assert(m~=nil,"\086\097\108\117\101 \099\097\110\110\111\116 \098\101 \110\105\108")q[v]=m if T[v]then for v,u in pairs(T[v])do spawn(function()u(m)end)end end end function X.UpdateAsync(u,v,m)local d=m(q[v])assert(d~=nil,"\086\097\108\117\101 \099\097\110\110\111\116 \098\101 \110\105\108")q[v]=d if T[v]then for v,m in pairs(T[v])do spawn(function()m(d)end)end end end function X.GetAsync(m,v)return q[v]end function X.RemoveAsync(m,v)q[v]=nil if T[v]then for v,m in pairs(T[v])do spawn(function()m(nil)end)end end end function X.IncrementAsync(u,v,m)if m==nil then m=1 end assert(type(m)=="\110\117\109\098\101\114","\067\097\110 \111\110\108\121 \105\110\099\114\101\109\101\110\116 \110\117\109\098\101\114\115")u:UpdateAsync(v,function(v)if v==nil then return v end assert(type(v)=="\110\117\109\098\101\114","\067\097\110 \111\110\108\121 \105\110\099\114\101\109\101\110\116 \110\117\109\098\101\114\115")return v+m end)end function X.OnUpdate(u,v,m)assert(type(m)=="\102\117\110\099\116\105\111\110","\085\112\100\097\116\101 \102\117\110\099\116\105\111\110 \097\114\103\117\109\101\110\116 \109\117\115\116 \098\101 \097 \102\117\110\099\116\105\111\110")if not T[v]then T[v]={m}else table.insert(T[v],m)end end if not d[m]then d[m]={}end d[m][v]=X return X end function m.GetGlobalDataStore(v)return v:GetDataStore("\103\108\111\098\097\108","\103\108\111\098\097\108")end function m.GetOrderedDataStore(u,v,m)local q=u:GetDataStore(v,m)local d={}local X={}function X.GetAsync(m,v)return q:GetAsync(v)end function X.SetAsync(u,v,m)assert(type(m)=="\110\117\109\098\101\114","\086\097\108\117\101 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")q:SetAsync(v,m)d[v]=m end function X.UpdateAsync(u,v,m)q:UpdateAsync(v,function(u)local q=m(u)assert(type(q)=="\110\117\109\098\101\114","\086\097\108\117\101 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")d[v]=q return q end)end function X.IncrementAsync(u,v,m)q:IncrementAsync(v,m)d[v]=(d[v]or 0)+m end function X.RemoveAsync(m,v)q:RemoveAsync(v)d[v]=nil end function X.GetSortedAsync(X,v,m,u,q)assert(type(m)=="\110\117\109\098\101\114"and math.floor(m)>0,"\080\097\103\101\083\105\122\101 \109\117\115\116 \098\101 \097\110 \105\110\116\101\103\101\114 \097\110\100 \103\114\101\097\116\101\114 \116\104\097\110 \048")assert(u==nil or type(u)=="\110\117\109\098\101\114","\077\105\110\086\097\108\117\101 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")assert(q==nil or type(q)=="\110\117\109\098\101\114","\077\097\120\086\097\108\117\101 \109\117\115\116 \098\101 \097 \110\117\109\098\101\114")if u and q then assert(u<=q,"\077\105\110\086\097\108\117\101 \109\117\115\116 \098\101 \108\101\115\115 \111\114 \101\113\117\097\108 \116\111 \077\097\120\086\097\108\117\101")end local T={}for v,m in pairs(d)do local d=(not u or m>=u)and(not q or m<=q)if d then table.insert(T,{key=v,value=m})end end table.sort(T,v and function(v,m)return v.value<m.value end or function(v,m)return m.value<v.value end)m=math.floor(m)local F={IsFinished=false}for v,u in pairs(T)do local q=math.ceil(v/m)local d=F[q]if not d then d={}F[q]=d end local X=(v-1)%m+1 d[X]=u end do local v=1 function F.GetCurrentPage(m)return m[v]end function F.AdvanceToNextPageAsync(m)local u=#F if v<u then v=v+1 end m.IsFinished=v>=u end end return F end return X end function m.GetRequestBudgetForRequestType(m,v)return q:GetRequestBudgetForRequestType(v)end u.__metatable=true u.__index=function(v,u)return m[u]or q[u]end u.__newindex=function()error("\067\097\110\110\111\116 \101\100\105\116 \077\111\099\107\068\097\116\097\083\116\111\114\101\083\101\114\118\105\099\101")end setmetatable(v,u)return v
-- Mock DataStoreService
-- Crazyman32
-- August 20, 2014


--[[
	
	USAGE EXAMPLE:
	
		--------
		local dataStoreService = game:GetService("DataStoreService")
		
		-- Mock service if the game is offline:
		if (game.PlaceId == 0) then
			dataStoreService = require(game.ServerStorage.MockDataStoreService)
		end
		
		-- dataStoreService will act exactly like the real one
		--------
	
	The mocked data store service should function exactly like the
	real service. What the mocked service does is "override" the
	core methods, such as GetAsync. If you try to index a property
	that hasn't been overridden (such as dataStoreService.Name), it
	will reference the actual property in the real dataStoreService.
	
	NOTE:
		This has been created based off of the DataStoreService on
		August 20, 2014. If a change has been made to the service,
		this mocked version will not reflect the changes.
	
--]]



local DataStoreService = {}
local API = {}
local MT = {}


-----------------------------------------------------------------------------------------------------------

local realDataStoreService = game:GetService("DataStoreService")
local allStores = {}

if (game:GetService("Players").LocalPlayer) then
	warn("Mocked DataStoreService is functioning on the client: The real DataStoreService will not work on the client")
end


-----------------------------------------------------------------------------------------------------------
-- API:

function API:GetDataStore(name, scope)
	assert(type(name) == "string", "DataStore name must be a string; got" .. type(name))
	assert(type(scope) == "string" or scope == nil, "DataStore scope must be a string; got" .. type(scope))
	scope = (scope or "global")
	if (allStores[scope] and allStores[scope][name]) then
		return allStores[scope][name]
	end
	local data = {}
	local d = {}
	local updateListeners = {}
	function d:SetAsync(k, v)
		assert(v ~= nil, "Value cannot be nil")
		data[k] = v
		if (updateListeners[k]) then
			for _,f in pairs(updateListeners[k]) do
				spawn(function() f(v) end)
			end
		end
	end
	function d:UpdateAsync(k, func)
		local v = func(data[k])
		assert(v ~= nil, "Value cannot be nil")
		data[k] = v
		if (updateListeners[k]) then
			for _,f in pairs(updateListeners[k]) do
				spawn(function() f(v) end)
			end
		end
	end
	function d:GetAsync(k)
		return data[k]
	end
	function d:RemoveAsync(k)
		data[k] = nil
		if (updateListeners[k]) then
			for _,f in pairs(updateListeners[k]) do
				spawn(function() f(nil) end)
			end
		end
	end
	function d:IncrementAsync(k, delta)
		if (delta == nil) then delta = 1 end
		assert(type(delta) == "number", "Can only increment numbers")
		self:UpdateAsync(k, function(num)
			if (num == nil) then
				return num
			end
			assert(type(num) == "number", "Can only increment numbers")
			return (num + delta)
		end)
	end
	function d:OnUpdate(k, onUpdateFunc)
		assert(type(onUpdateFunc) == "function", "Update function argument must be a function")
		if (not updateListeners[k]) then
			updateListeners[k] = {onUpdateFunc}
		else
			table.insert(updateListeners[k], onUpdateFunc)
		end
	end
	if (not allStores[scope]) then
		allStores[scope] = {}
	end
	allStores[scope][name] = d
	return d
end


function API:GetGlobalDataStore()
	return self:GetDataStore("global", "global")
end


function API:GetOrderedDataStore(name, scope)
	local dataStore = self:GetDataStore(name, scope)
	local allData = {}
	local d = {}
	function d:GetAsync(k)
		return dataStore:GetAsync(k)
	end
	function d:SetAsync(k, v)
		assert(type(v) == "number", "Value must be a number")
		dataStore:SetAsync(k, v)
		allData[k] = v
	end
	function d:UpdateAsync(k, func)
		dataStore:UpdateAsync(k, function(oldValue)
			local v = func(oldValue)
			assert(type(v) == "number", "Value must be a number")
			allData[k] = v
			return v
		end)
	end
	function d:IncrementAsync(k, delta)
		dataStore:IncrementAsync(k, delta)
		allData[k] = ((allData[k] or 0) + delta)
	end
	function d:RemoveAsync(k)
		dataStore:RemoveAsync(k)
		allData[k] = nil
	end
	function d:GetSortedAsync(isAscending, pageSize, minValue, maxValue)
		assert(type(pageSize) == "number" and math.floor(pageSize) > 0, "PageSize must be an integer and greater than 0")
		assert(minValue == nil or type(minValue) == "number", "MinValue must be a number")
		assert(maxValue == nil or type(maxValue) == "number", "MaxValue must be a number")
		if (minValue and maxValue) then
			assert(minValue <= maxValue, "MinValue must be less or equal to MaxValue")
		end
		local data = {}
		for k,v in pairs(allData) do
			local pass = ((not minValue or v >= minValue) and (not maxValue or v <= maxValue))
			if (pass) then
				table.insert(data, {key = k, value = v})
			end
		end
		table.sort(data,
			(isAscending and
				function(a, b) return (a.value < b.value) end
			or
				function(a, b) return (b.value < a.value) end
			)
		)
		pageSize = math.floor(pageSize)
		local pages = {IsFinished = false}
		for i,v in pairs(data) do
			local pageNum = math.ceil(i / pageSize)
			local page = pages[pageNum]
			if (not page) then
				page = {}
				pages[pageNum] = page
			end
			local index = (((i - 1) % pageSize) + 1)
			page[index] = v
		end
		do
			local currentPage = 1
			function pages:GetCurrentPage()
				return self[currentPage]
			end
			function pages:AdvanceToNextPageAsync()
				local numPages = #pages
				if (currentPage < numPages) then
					currentPage = (currentPage + 1)
				end
				self.IsFinished = (currentPage >= numPages)
			end
		end
		return pages
	end
	return d
end


function API:GetRequestBudgetForRequestType(requestType)
	return realDataStoreService:GetRequestBudgetForRequestType(requestType)
end


-----------------------------------------------------------------------------------------------------------
-- Metatable:

MT.__metatable = true
MT.__index = function(tbl, index)
	return (API[index] or realDataStoreService[index])
end
MT.__newindex = function()
	error("Cannot edit MockDataStoreService")
end
setmetatable(DataStoreService, MT)

-----------------------------------------------------------------------------------------------------------

return DataStoreService