local O={}O.__index=O local g=require(script.Parent.MockDataStoreManager)local Y=require(script.Parent.MockDataStorePages)local M=require(script.Parent.MockDataStoreUtils)local D=require(script.Parent.MockDataStoreConstants)local k=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local h=Random.new()function O.OnUpdate(k,O,Y)O=M.preprocessKey(O)if type(O)~="\115\116\114\105\110\103"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\079\110\085\112\100\097\116\101\' \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(O)),2)elseif type(Y)~="\102\117\110\099\116\105\111\110"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\050 \116\111 \'\079\110\085\112\100\097\116\101\' \040\102\117\110\099\116\105\111\110 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(Y)),2)elseif#O==0 then error("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\079\110\085\112\100\097\116\101\' \040\107\101\121 \110\097\109\101 \099\097\110\'\116 \098\101 \101\109\112\116\121\041",2)elseif#O>D.MAX_LENGTH_KEY then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\079\110\085\112\100\097\116\101\' \040\107\101\121 \110\097\109\101 \101\120\099\101\101\100\115 \037\100 \099\104\097\114\097\099\116\101\114 \108\105\109\105\116\041"):format(D.MAX_LENGTH_KEY),2)end M.simulateErrorCheck("\079\110\085\112\100\097\116\101")local X=g.YieldForBudget(function()warn(("\079\110\085\112\100\097\116\101 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100 \100\117\101 \116\111 \108\097\099\107 \111\102 \098\117\100\103\101\116\046 \084\114\121 \115\101\110\100\105\110\103 \102\101\119\101\114 \114\101\113\117\101\115\116\115\046 \075\101\121 \061 \037\115"):format(O))end,{Enum.DataStoreRequestType.OnUpdate})if not X then error("\079\110\085\112\100\097\116\101 \114\101\106\101\099\116\101\100 \119\105\116\104 \101\114\114\111\114 \040\114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \098\117\116 \116\104\114\111\116\116\108\101\100 \113\117\101\117\101 \119\097\115 \102\117\108\108\041",2)end M.logMethod(k,"\079\110\085\112\100\097\116\101",O)return k.__event.Event:Connect(function(g,M)if g==O then if D.YIELD_TIME_UPDATE_MAX>0 then task.wait(h:NextNumber(D.YIELD_TIME_UPDATE_MIN,D.YIELD_TIME_UPDATE_MAX))end Y(M)end end)end function O.GetAsync(Y,O)O=M.preprocessKey(O)if type(O)~="\115\116\114\105\110\103"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\071\101\116\065\115\121\110\099\' \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(O)),2)elseif#O==0 then error("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\071\101\116\065\115\121\110\099\' \040\107\101\121 \110\097\109\101 \099\097\110\'\116 \098\101 \101\109\112\116\121\041",2)elseif#O>D.MAX_LENGTH_KEY then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\071\101\116\065\115\121\110\099\' \040\107\101\121 \110\097\109\101 \101\120\099\101\101\100\115 \037\100 \099\104\097\114\097\099\116\101\114 \108\105\109\105\116\041"):format(D.MAX_LENGTH_KEY),2)end if Y.__getCache[O]and tick()-Y.__getCache[O]<D.GET_COOLDOWN then return Y.__data[O]end M.simulateErrorCheck("\071\101\116\065\115\121\110\099")local k=g.YieldForBudget(function()warn(("\071\101\116\065\115\121\110\099 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100 \100\117\101 \116\111 \108\097\099\107 \111\102 \098\117\100\103\101\116\046 \084\114\121 \115\101\110\100\105\110\103 \102\101\119\101\114 \114\101\113\117\101\115\116\115\046 \075\101\121 \061 \037\115"):format(O))end,{Enum.DataStoreRequestType.GetAsync})if not k then error("\071\101\116\065\115\121\110\099 \114\101\106\101\099\116\101\100 \119\105\116\104 \101\114\114\111\114 \040\114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \098\117\116 \116\104\114\111\116\116\108\101\100 \113\117\101\117\101 \119\097\115 \102\117\108\108\041",2)end local h=Y.__data[O]M.simulateYield()Y.__getCache[O]=tick()M.logMethod(Y,"\071\101\116\065\115\121\110\099",O)return h end function O.IncrementAsync(k,O,Y)O=M.preprocessKey(O)if type(O)~="\115\116\114\105\110\103"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\073\110\099\114\101\109\101\110\116\065\115\121\110\099\' \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(O)),2)elseif Y~=nil and type(Y)~="\110\117\109\098\101\114"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\050 \116\111 \'\073\110\099\114\101\109\101\110\116\065\115\121\110\099\' \040\110\117\109\098\101\114 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(Y)),2)elseif#O==0 then error("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\073\110\099\114\101\109\101\110\116\065\115\121\110\099\' \040\107\101\121 \110\097\109\101 \099\097\110\'\116 \098\101 \101\109\112\116\121\041",2)elseif#O>D.MAX_LENGTH_KEY then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\073\110\099\114\101\109\101\110\116\065\115\121\110\099\' \040\107\101\121 \110\097\109\101 \101\120\099\101\101\100\115 \037\100 \099\104\097\114\097\099\116\101\114 \108\105\109\105\116\041"):format(D.MAX_LENGTH_KEY),2)end M.simulateErrorCheck("\073\110\099\114\101\109\101\110\116\065\115\121\110\099")local h if k.__writeLock[O]or tick()-(k.__writeCache[O]or 0)<D.WRITE_COOLDOWN then h=g.YieldForWriteLockAndBudget(function()warn(("\073\110\099\114\101\109\101\110\116\065\115\121\110\099 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \097 \107\101\121 \099\097\110 \111\110\108\121 \098\101 \119\114\105\116\116\101\110 \116\111 \111\110\099\101 \101\118\101\114\121 \037\100 \115\101\099\111\110\100\115\046 \075\101\121 \061 \037\115"):format(D.WRITE_COOLDOWN,O))end,O,k.__writeLock,k.__writeCache,{Enum.DataStoreRequestType.SetIncrementSortedAsync})else k.__writeLock[O]=true h=g.YieldForBudget(function()warn(("\073\110\099\114\101\109\101\110\116\065\115\121\110\099 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100 \100\117\101 \116\111 \108\097\099\107 \111\102 \098\117\100\103\101\116\046 \084\114\121 \115\101\110\100\105\110\103 \102\101\119\101\114 \114\101\113\117\101\115\116\115\046 \075\101\121 \061 \037\115"):format(O))end,{Enum.DataStoreRequestType.SetIncrementSortedAsync})k.__writeLock[O]=nil end if not h then error("\073\110\099\114\101\109\101\110\116\065\115\121\110\099 \114\101\106\101\099\116\101\100 \119\105\116\104 \101\114\114\111\114 \040\114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \098\117\116 \116\104\114\111\116\116\108\101\100 \113\117\101\117\101 \119\097\115 \102\117\108\108\041",2)end local X=k.__data[O]if X~=nil and(type(X)~="\110\117\109\098\101\114"or X%1~=0)then M.simulateYield()error("\073\110\099\114\101\109\101\110\116\065\115\121\110\099 \114\101\106\101\099\116\101\100 \119\105\116\104 \101\114\114\111\114 \040\099\097\110\110\111\116 \105\110\099\114\101\109\101\110\116 \110\111\110-\105\110\116\101\103\101\114 \118\097\108\117\101\041",2)end k.__writeLock[O]=true Y=Y and math.floor(Y+.5)or 1 if X==nil then k.__data[O]=Y k.__ref[O]={Key=O,Value=k.__data[O]}table.insert(k.__sorted,k.__ref[O])k.__changed=true k.__event:Fire(O,k.__data[O])elseif Y~=0 then k.__data[O]=k.__data[O]+Y k.__ref[O].Value=k.__data[O]k.__changed=true k.__event:Fire(O,k.__data[O])end local z=k.__data[O]M.simulateYield()k.__writeLock[O]=nil k.__writeCache[O]=tick()k.__getCache[O]=tick()M.logMethod(k,"\073\110\099\114\101\109\101\110\116\065\115\121\110\099",O,z,Y)return z end function O.RemoveAsync(Y,O)O=M.preprocessKey(O)if type(O)~="\115\116\114\105\110\103"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\082\101\109\111\118\101\065\115\121\110\099\' \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(type(O)),2)elseif#O==0 then error("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\082\101\109\111\118\101\065\115\121\110\099\' \040\107\101\121 \110\097\109\101 \099\097\110\'\116 \098\101 \101\109\112\116\121\041",2)elseif#O>D.MAX_LENGTH_KEY then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\082\101\109\111\118\101\065\115\121\110\099\' \040\107\101\121 \110\097\109\101 \101\120\099\101\101\100\115 \037\100 \099\104\097\114\097\099\116\101\114 \108\105\109\105\116\041"):format(D.MAX_LENGTH_KEY),2)end M.simulateErrorCheck("\082\101\109\111\118\101\065\115\121\110\099")local k if Y.__writeLock[O]or tick()-(Y.__writeCache[O]or 0)<D.WRITE_COOLDOWN then k=g.YieldForWriteLockAndBudget(function()warn(("\082\101\109\111\118\101\065\115\121\110\099 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \097 \107\101\121 \099\097\110 \111\110\108\121 \098\101 \119\114\105\116\116\101\110 \116\111 \111\110\099\101 \101\118\101\114\121 \037\100 \115\101\099\111\110\100\115\046 \075\101\121 \061 \037\115"):format(D.WRITE_COOLDOWN,O))end,O,Y.__writeLock,Y.__writeCache,{Enum.DataStoreRequestType.SetIncrementSortedAsync})else Y.__writeLock[O]=true k=g.YieldForBudget(function()warn(("\082\101\109\111\118\101\065\115\121\110\099 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100 \100\117\101 \116\111 \108\097\099\107 \111\102 \098\117\100\103\101\116\046 \084\114\121 \115\101\110\100\105\110\103 \102\101\119\101\114 \114\101\113\117\101\115\116\115\046 \075\101\121 \061 \037\115"):format(O))end,{Enum.DataStoreRequestType.SetIncrementSortedAsync})Y.__writeLock[O]=nil end if not k then error("\082\101\109\111\118\101\065\115\121\110\099 \114\101\106\101\099\116\101\100 \119\105\116\104 \101\114\114\111\114 \040\114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \098\117\116 \116\104\114\111\116\116\108\101\100 \113\117\101\117\101 \119\097\115 \102\117\108\108\041",2)end Y.__writeLock[O]=true local h=Y.__data[O]if h~=nil then Y.__data[O]=nil Y.__ref[O]=nil for g,M in pairs(Y.__sorted)do if M.Key==O then table.remove(Y.__sorted,g)break end end Y.__event:Fire(O,nil)end M.simulateYield()Y.__writeLock[O]=nil Y.__writeCache[O]=tick()M.logMethod(Y,"\082\101\109\111\118\101\065\115\121\110\099",O,h)return h end function O.SetAsync(k,O,Y)O=M.preprocessKey(O)if type(O)~="\115\116\114\105\110\103"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\083\101\116\065\115\121\110\099\' \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(O)),2)elseif#O==0 then error("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\083\101\116\065\115\121\110\099\' \040\107\101\121 \110\097\109\101 \099\097\110\'\116 \098\101 \101\109\112\116\121\041",2)elseif#O>D.MAX_LENGTH_KEY then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\083\101\116\065\115\121\110\099\' \040\107\101\121 \110\097\109\101 \101\120\099\101\101\100\115 \037\100 \099\104\097\114\097\099\116\101\114 \108\105\109\105\116\041"):format(D.MAX_LENGTH_KEY),2)elseif type(Y)~="\110\117\109\098\101\114"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\050 \116\111 \'\083\101\116\065\115\121\110\099\' \040\110\117\109\098\101\114 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(Y)),2)elseif Y%1~=0 then error("\098\097\100 \097\114\103\117\109\101\110\116 \035\050 \116\111 \'\083\101\116\065\115\121\110\099\' \040\099\097\110\110\111\116 \115\116\111\114\101 \110\111\110-\105\110\116\101\103\101\114 \118\097\108\117\101\115 \105\110 \079\114\100\101\114\101\100\068\097\116\097\083\116\111\114\101\041",2)end M.simulateErrorCheck("\083\101\116\065\115\121\110\099")local h if k.__writeLock[O]or tick()-(k.__writeCache[O]or 0)<D.WRITE_COOLDOWN then h=g.YieldForWriteLockAndBudget(function()warn(("\083\101\116\065\115\121\110\099 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \097 \107\101\121 \099\097\110 \111\110\108\121 \098\101 \119\114\105\116\116\101\110 \116\111 \111\110\099\101 \101\118\101\114\121 \037\100 \115\101\099\111\110\100\115\046 \075\101\121 \061 \037\115"):format(D.WRITE_COOLDOWN,O))end,O,k.__writeLock,k.__writeCache,{Enum.DataStoreRequestType.SetIncrementSortedAsync})else k.__writeLock[O]=true h=g.YieldForBudget(function()warn(("\083\101\116\065\115\121\110\099 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100 \100\117\101 \116\111 \108\097\099\107 \111\102 \098\117\100\103\101\116\046 \084\114\121 \115\101\110\100\105\110\103 \102\101\119\101\114 \114\101\113\117\101\115\116\115\046 \075\101\121 \061 \037\115"):format(O))end,{Enum.DataStoreRequestType.SetIncrementSortedAsync})k.__writeLock[O]=nil end if not h then error("\083\101\116\065\115\121\110\099 \114\101\106\101\099\116\101\100 \119\105\116\104 \101\114\114\111\114 \040\114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \098\117\116 \116\104\114\111\116\116\108\101\100 \113\117\101\117\101 \119\097\115 \102\117\108\108\041",2)end k.__writeLock[O]=true local X=k.__data[O]if X==nil then k.__data[O]=Y k.__ref[O]={Key=O;Value=Y}table.insert(k.__sorted,k.__ref[O])k.__changed=true k.__event:Fire(O,k.__data[O])elseif X~=Y then k.__data[O]=Y k.__ref[O].Value=Y k.__changed=true k.__event:Fire(O,k.__data[O])end M.simulateYield()k.__writeLock[O]=nil k.__writeCache[O]=tick()M.logMethod(k,"\083\101\116\065\115\121\110\099",O,k.__data[O])return Y end function O.UpdateAsync(k,O,Y)O=M.preprocessKey(O)if type(O)~="\115\116\114\105\110\103"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\085\112\100\097\116\101\065\115\121\110\099\' \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(O)),2)elseif type(Y)~="\102\117\110\099\116\105\111\110"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\050 \116\111 \'\085\112\100\097\116\101\065\115\121\110\099\' \040\102\117\110\099\116\105\111\110 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(Y)),2)elseif#O==0 then error("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\085\112\100\097\116\101\065\115\121\110\099\' \040\107\101\121 \110\097\109\101 \099\097\110\'\116 \098\101 \101\109\112\116\121\041",2)elseif#O>D.MAX_LENGTH_KEY then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\085\112\100\097\116\101\065\115\121\110\099\' \040\107\101\121 \110\097\109\101 \101\120\099\101\101\100\115 \037\100 \099\104\097\114\097\099\116\101\114 \108\105\109\105\116\041"):format(D.MAX_LENGTH_KEY),2)end M.simulateErrorCheck("\085\112\100\097\116\101\065\115\121\110\099")local h if k.__writeLock[O]or tick()-(k.__writeCache[O]or 0)<D.WRITE_COOLDOWN then h=g.YieldForWriteLockAndBudget(function()warn(("\085\112\100\097\116\101\065\115\121\110\099 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \097 \107\101\121 \099\097\110 \111\110\108\121 \098\101 \119\114\105\116\116\101\110 \116\111 \111\110\099\101 \101\118\101\114\121 \037\100 \115\101\099\111\110\100\115\046 \075\101\121 \061 \037\115"):format(D.WRITE_COOLDOWN,O))end,O,k.__writeLock,k.__writeCache,{Enum.DataStoreRequestType.SetIncrementSortedAsync})else k.__writeLock[O]=true local Y if k.__getCache[O]and tick()-k.__getCache[O]<D.GET_COOLDOWN then Y={Enum.DataStoreRequestType.SetIncrementSortedAsync}else Y={Enum.DataStoreRequestType.GetAsync;Enum.DataStoreRequestType.SetIncrementSortedAsync}end h=g.YieldForBudget(function()warn(("\085\112\100\097\116\101\065\115\121\110\099 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100 \100\117\101 \116\111 \108\097\099\107 \111\102 \098\117\100\103\101\116\046 \084\114\121 \115\101\110\100\105\110\103 \102\101\119\101\114 \114\101\113\117\101\115\116\115\046 \075\101\121 \061 \037\115"):format(O))end,Y)k.__writeLock[O]=nil end if not h then error("\085\112\100\097\116\101\065\115\121\110\099 \114\101\106\101\099\116\101\100 \119\105\116\104 \101\114\114\111\114 \040\114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \098\117\116 \116\104\114\111\116\116\108\101\100 \113\117\101\117\101 \119\097\115 \102\117\108\108\041",2)end local X=Y(k.__data[O])if X==nil then M.simulateYield()return nil end if type(X)~="\110\117\109\098\101\114"or X%1~=0 then error("\085\112\100\097\116\101\065\115\121\110\099 \114\101\106\101\099\116\101\100 \119\105\116\104 \101\114\114\111\114 \040\114\101\115\117\108\116\105\110\103 \110\111\110-\105\110\116\101\103\101\114 \118\097\108\117\101 \099\097\110\'\116 \098\101 \115\116\111\114\101\100 \105\110 \079\114\100\101\114\101\100\068\097\116\097\083\116\111\114\101\041",2)end k.__writeLock[O]=true local z=k.__data[O]if z==nil then k.__data[O]=X k.__ref[O]={Key=O;Value=X}table.insert(k.__sorted,k.__ref[O])k.__changed=true k.__event:Fire(O,k.__data[O])elseif z~=X then k.__data[O]=X k.__ref[O].Value=X k.__changed=true k.__event:Fire(O,k.__data[O])end M.simulateYield()k.__writeLock[O]=nil k.__writeCache[O]=tick()k.__getCache[O]=tick()M.logMethod(k,"\085\112\100\097\116\101\065\115\121\110\099",O,X)return X end function O.GetSortedAsync(z,O,k,h,X)if type(O)~="\098\111\111\108\101\097\110"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\071\101\116\083\111\114\116\101\100\065\115\121\110\099\' \040\098\111\111\108\101\097\110 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(O)),2)elseif type(k)~="\110\117\109\098\101\114"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\050 \116\111 \'\071\101\116\083\111\114\116\101\100\065\115\121\110\099\' \040\110\117\109\098\101\114 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(k)),2)end k=math.floor(k+.5)if k<=0 or k>D.MAX_PAGE_SIZE then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\050 \116\111 \'\071\101\116\083\111\114\116\101\100\065\115\121\110\099\' \040\112\097\103\101 \115\105\122\101 \109\117\115\116 \098\101 \097\110 \105\110\116\101\103\101\114 \097\098\111\118\101 \048 \097\110\100 \098\101\108\111\119 \111\114 \101\113\117\097\108 \116\111 \037\100\041"):format(D.MAX_PAGE_SIZE),2)end if h~=nil then if type(h)~="\110\117\109\098\101\114"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\051 \116\111 \'\071\101\116\083\111\114\116\101\100\065\115\121\110\099\' \040\110\117\109\098\101\114 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(h)),2)elseif h%1~=0 then error("\098\097\100 \097\114\103\117\109\101\110\116 \035\051 \116\111 \'\071\101\116\083\111\114\116\101\100\065\115\121\110\099\' \040\109\105\110\105\109\117\109 \116\104\114\101\115\104\111\108\100 \109\117\115\116 \098\101 \097\110 \105\110\116\101\103\101\114\041",2)end else h=-math.huge end if X~=nil then if type(X)~="\110\117\109\098\101\114"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\052 \116\111 \'\071\101\116\083\111\114\116\101\100\065\115\121\110\099\' \040\110\117\109\098\101\114 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(X)),2)elseif X%1~=0 then error("\098\097\100 \097\114\103\117\109\101\110\116 \035\052 \116\111 \'\071\101\116\083\111\114\116\101\100\065\115\121\110\099\' \040\109\097\120\105\109\117\109 \116\104\114\101\115\104\111\108\100 \109\117\115\116 \098\101 \097\110 \105\110\116\101\103\101\114\041",2)end else X=math.huge end M.simulateErrorCheck("\071\101\116\083\111\114\116\101\100\065\115\121\110\099")local l=g.YieldForBudget(function()warn("\071\101\116\083\111\114\116\101\100\065\115\121\110\099 \114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100 \100\117\101 \116\111 \108\097\099\107 \111\102 \098\117\100\103\101\116\046 \084\114\121 \115\101\110\100\105\110\103 \102\101\119\101\114 \114\101\113\117\101\115\116\115\046")end,{Enum.DataStoreRequestType.GetSortedAsync})if not l then error("\071\101\116\083\111\114\116\101\100\065\115\121\110\099 \114\101\106\101\099\116\101\100 \119\105\116\104 \101\114\114\111\114 \040\114\101\113\117\101\115\116 \119\097\115 \116\104\114\111\116\116\108\101\100\044 \098\117\116 \116\104\114\111\116\116\108\101\100 \113\117\101\117\101 \119\097\115 \102\117\108\108\041",2)end if h>X then M.simulateYield()error("\071\101\116\083\111\114\116\101\100\065\115\121\110\099 \114\101\106\101\099\116\101\100 \119\105\116\104 \101\114\114\111\114 \040\109\105\110\105\109\117\109 \116\104\114\101\115\104\111\108\100 \105\115 \104\105\103\104\101\114 \116\104\097\110 \109\097\120\105\109\117\109 \116\104\114\101\115\104\111\108\100\041",2)end if z.__changed then table.sort(z.__sorted,function(O,g)return O.Value<g.Value end)z.__changed=false end local G={}if O then local O=1 while z.__sorted[O]and z.__sorted[O].Value<h do O=O+1 end while z.__sorted[O]and z.__sorted[O].Value<=X do table.insert(G,{key=z.__sorted[O].Key;value=z.__sorted[O].Value})O=O+1 end else local O=#z.__sorted while O>0 and z.__sorted[O].Value>X do O=O-1 end while O>0 and z.__sorted[O].Value>=h do table.insert(G,{key=z.__sorted[O].Key,value=z.__sorted[O].Value})O=O-1 end end M.simulateYield()M.logMethod(z,"\071\101\116\083\111\114\116\101\100\065\115\121\110\099")return setmetatable({__datastore=z;__currentPage=1,__pageSize=k;__results=G,IsFinished=#G<=k},Y)end function O.ExportToJSON(O)return k:JSONEncode(O.__data)end function O.ImportFromJSON(D,O,Y)local h if type(O)=="\115\116\114\105\110\103"then local g,Y=pcall(function()return k:JSONDecode(O)end)if not g then error("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\073\109\112\111\114\116\070\114\111\109\074\083\079\078\' \040\115\116\114\105\110\103 \105\115 \110\111\116 \118\097\108\105\100 \106\115\111\110\041",2)end h=Y elseif type(O)=="\116\097\098\108\101"then h=O else error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \'\073\109\112\111\114\116\070\114\111\109\074\083\079\078\' \040\115\116\114\105\110\103 \111\114 \116\097\098\108\101 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(O)),2)end if Y~=nil and type(Y)~="\098\111\111\108\101\097\110"then error(("\098\097\100 \097\114\103\117\109\101\110\116 \035\050 \116\111 \'\073\109\112\111\114\116\070\114\111\109\074\083\079\078\' \040\098\111\111\108\101\097\110 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041"):format(typeof(Y)),2)end M.importPairsFromTable(h,D.__data,g.GetDataInterface(D.__data),Y==false and function() end or warn,"\073\109\112\111\114\116\070\114\111\109\074\083\079\078",("\079\114\100\101\114\101\100\068\097\116\097\083\116\111\114\101 \062 \037\115 \062 \037\115"):format(D.__name,D.__scope),true)end return O
--[[
	MockOrderedDataStore.lua
	This module implements the API and functionality of Roblox's OrderedDataStore class.

	This module is licensed under APLv2, refer to the LICENSE file or:
	buildthomas/MockDataStoreService/blob/master/LICENSE
]]

local MockOrderedDataStore = {}
MockOrderedDataStore.__index = MockOrderedDataStore

local MockDataStoreManager = require(script.Parent.MockDataStoreManager)
local MockDataStorePages = require(script.Parent.MockDataStorePages)
local Utils = require(script.Parent.MockDataStoreUtils)
local Constants = require(script.Parent.MockDataStoreConstants)
local HttpService = game:GetService("HttpService") -- for json encode/decode

local rand = Random.new()

function MockOrderedDataStore:OnUpdate(key, callback)
	key = Utils.preprocessKey(key)
	if type(key) ~= "string" then
		error(("bad argument #1 to 'OnUpdate' (string expected, got %s)"):format(typeof(key)), 2)
	elseif type(callback) ~= "function" then
		error(("bad argument #2 to 'OnUpdate' (function expected, got %s)"):format(typeof(callback)), 2)
	elseif #key == 0 then
		error("bad argument #1 to 'OnUpdate' (key name can't be empty)", 2)
	elseif #key > Constants.MAX_LENGTH_KEY then
		error(("bad argument #1 to 'OnUpdate' (key name exceeds %d character limit)"):format(Constants.MAX_LENGTH_KEY), 2)
	end

	Utils.simulateErrorCheck("OnUpdate")

	local success = MockDataStoreManager.YieldForBudget(
		function()
			warn(("OnUpdate request was throttled due to lack of budget. Try sending fewer requests. Key = %s"):format(key))
		end,
		{Enum.DataStoreRequestType.OnUpdate}
	)

	if not success then
		error("OnUpdate rejected with error (request was throttled, but throttled queue was full)", 2)
	end

	Utils.logMethod(self, "OnUpdate", key)

	return self.__event.Event:Connect(function(k, v)
		if k == key then
			if Constants.YIELD_TIME_UPDATE_MAX > 0 then
				task.wait(rand:NextNumber(Constants.YIELD_TIME_UPDATE_MIN, Constants.YIELD_TIME_UPDATE_MAX))
			end
			callback(v) -- v was implicitly deep-copied
		end
	end)
end

function MockOrderedDataStore:GetAsync(key)
	key = Utils.preprocessKey(key)
	if type(key) ~= "string" then
		error(("bad argument #1 to 'GetAsync' (string expected, got %s)"):format(typeof(key)), 2)
	elseif #key == 0 then
		error("bad argument #1 to 'GetAsync' (key name can't be empty)", 2)
	elseif #key > Constants.MAX_LENGTH_KEY then
		error(("bad argument #1 to 'GetAsync' (key name exceeds %d character limit)"):format(Constants.MAX_LENGTH_KEY), 2)
	end

	if self.__getCache[key] and tick() - self.__getCache[key] < Constants.GET_COOLDOWN then
		return self.__data[key]
	end

	Utils.simulateErrorCheck("GetAsync")

	local success = MockDataStoreManager.YieldForBudget(
		function()
			warn(("GetAsync request was throttled due to lack of budget. Try sending fewer requests. Key = %s"):format(key))
		end,
		{Enum.DataStoreRequestType.GetAsync}
	)

	if not success then
		error("GetAsync rejected with error (request was throttled, but throttled queue was full)", 2)
	end

	local retValue = self.__data[key]

	Utils.simulateYield()

	self.__getCache[key] = tick()

	Utils.logMethod(self, "GetAsync", key)

	return retValue
end

function MockOrderedDataStore:IncrementAsync(key, delta)
	key = Utils.preprocessKey(key)
	if type(key) ~= "string" then
		error(("bad argument #1 to 'IncrementAsync' (string expected, got %s)"):format(typeof(key)), 2)
	elseif delta ~= nil and type(delta) ~= "number" then
		error(("bad argument #2 to 'IncrementAsync' (number expected, got %s)"):format(typeof(delta)), 2)
	elseif #key == 0 then
		error("bad argument #1 to 'IncrementAsync' (key name can't be empty)", 2)
	elseif #key > Constants.MAX_LENGTH_KEY then
		error(("bad argument #1 to 'IncrementAsync' (key name exceeds %d character limit)")
			:format(Constants.MAX_LENGTH_KEY), 2)
	end

	Utils.simulateErrorCheck("IncrementAsync")

	local success

	if self.__writeLock[key] or tick() - (self.__writeCache[key] or 0) < Constants.WRITE_COOLDOWN then
		success = MockDataStoreManager.YieldForWriteLockAndBudget(
			function()
				warn(("IncrementAsync request was throttled, a key can only be written to once every %d seconds. Key = %s")
					:format(Constants.WRITE_COOLDOWN, key))
			end,
			key,
			self.__writeLock,
			self.__writeCache,
			{Enum.DataStoreRequestType.SetIncrementSortedAsync}
		)
	else
		self.__writeLock[key] = true
		success = MockDataStoreManager.YieldForBudget(
			function()
				warn(("IncrementAsync request was throttled due to lack of budget. Try sending fewer requests. Key = %s")
					:format(key))
			end,
			{Enum.DataStoreRequestType.SetIncrementSortedAsync}
		)
		self.__writeLock[key] = nil
	end

	if not success then
		error("IncrementAsync rejected with error (request was throttled, but throttled queue was full)", 2)
	end

	local old = self.__data[key]

	if old ~= nil and (type(old) ~= "number" or old % 1 ~= 0) then
		Utils.simulateYield()
		error("IncrementAsync rejected with error (cannot increment non-integer value)", 2)
	end

	self.__writeLock[key] = true

	delta = delta and math.floor(delta + .5) or 1

	if old == nil then
		self.__data[key] = delta
		self.__ref[key] = {Key = key, Value = self.__data[key]}
		table.insert(self.__sorted, self.__ref[key])
		self.__changed = true
		self.__event:Fire(key, self.__data[key])
	elseif delta ~= 0 then
		self.__data[key] = self.__data[key] + delta
		self.__ref[key].Value = self.__data[key]
		self.__changed = true
		self.__event:Fire(key, self.__data[key])
	end

	local retValue = self.__data[key]

	Utils.simulateYield()

	self.__writeLock[key] = nil
	self.__writeCache[key] = tick()

	self.__getCache[key] = tick()

	Utils.logMethod(self, "IncrementAsync", key, retValue, delta)

	return retValue
end

function MockOrderedDataStore:RemoveAsync(key)
	key = Utils.preprocessKey(key)
	if type(key) ~= "string" then
		error(("bad argument #1 to 'RemoveAsync' (string expected, got %s)"):format(type(key)), 2)
	elseif #key == 0 then
		error("bad argument #1 to 'RemoveAsync' (key name can't be empty)", 2)
	elseif #key > Constants.MAX_LENGTH_KEY then
		error(("bad argument #1 to 'RemoveAsync' (key name exceeds %d character limit)"):format(Constants.MAX_LENGTH_KEY), 2)
	end

	Utils.simulateErrorCheck("RemoveAsync")

	local success

	if self.__writeLock[key] or tick() - (self.__writeCache[key] or 0) < Constants.WRITE_COOLDOWN then
		success = MockDataStoreManager.YieldForWriteLockAndBudget(
			function()
				warn(("RemoveAsync request was throttled, a key can only be written to once every %d seconds. Key = %s")
					:format(Constants.WRITE_COOLDOWN, key))
			end,
			key,
			self.__writeLock,
			self.__writeCache,
			{Enum.DataStoreRequestType.SetIncrementSortedAsync}
		)
	else
		self.__writeLock[key] = true
		success = MockDataStoreManager.YieldForBudget(
			function()
				warn(("RemoveAsync request was throttled due to lack of budget. Try sending fewer requests. Key = %s")
					:format(key))
			end,
			{Enum.DataStoreRequestType.SetIncrementSortedAsync}
		)
		self.__writeLock[key] = nil
	end

	if not success then
		error("RemoveAsync rejected with error (request was throttled, but throttled queue was full)", 2)
	end

	self.__writeLock[key] = true

	local value = self.__data[key]

	if value ~= nil then
		self.__data[key] = nil
		self.__ref[key] = nil
		for i,v in pairs(self.__sorted) do
			if v.Key == key then
				table.remove(self.__sorted, i)
				break
			end
		end
		self.__event:Fire(key, nil)
	end

	Utils.simulateYield()

	self.__writeLock[key] = nil
	self.__writeCache[key] = tick()

	Utils.logMethod(self, "RemoveAsync", key, value)

	return value
end

function MockOrderedDataStore:SetAsync(key, value)
	key = Utils.preprocessKey(key)
	if type(key) ~= "string" then
		error(("bad argument #1 to 'SetAsync' (string expected, got %s)"):format(typeof(key)), 2)
	elseif #key == 0 then
		error("bad argument #1 to 'SetAsync' (key name can't be empty)", 2)
	elseif #key > Constants.MAX_LENGTH_KEY then
		error(("bad argument #1 to 'SetAsync' (key name exceeds %d character limit)"):format(Constants.MAX_LENGTH_KEY), 2)
	elseif type(value) ~= "number" then
		error(("bad argument #2 to 'SetAsync' (number expected, got %s)"):format(typeof(value)), 2)
	elseif value % 1 ~= 0 then
		error("bad argument #2 to 'SetAsync' (cannot store non-integer values in OrderedDataStore)", 2)
	end

	Utils.simulateErrorCheck("SetAsync")

	local success

	if self.__writeLock[key] or tick() - (self.__writeCache[key] or 0) < Constants.WRITE_COOLDOWN then
		success = MockDataStoreManager.YieldForWriteLockAndBudget(
			function()
				warn(("SetAsync request was throttled, a key can only be written to once every %d seconds. Key = %s")
					:format(Constants.WRITE_COOLDOWN, key))
			end,
			key,
			self.__writeLock,
			self.__writeCache,
			{Enum.DataStoreRequestType.SetIncrementSortedAsync}
		)
	else
		self.__writeLock[key] = true
		success = MockDataStoreManager.YieldForBudget(
			function()
				warn(("SetAsync request was throttled due to lack of budget. Try sending fewer requests. Key = %s")
					:format(key))
			end,
			{Enum.DataStoreRequestType.SetIncrementSortedAsync}
		)
		self.__writeLock[key] = nil
	end

	if not success then
		error("SetAsync rejected with error (request was throttled, but throttled queue was full)", 2)
	end

	self.__writeLock[key] = true

	local old = self.__data[key]

	if old == nil then
		self.__data[key] = value
		self.__ref[key] = {Key = key, Value = value}
		table.insert(self.__sorted, self.__ref[key])
		self.__changed = true
		self.__event:Fire(key, self.__data[key])
	elseif old ~= value then
		self.__data[key] = value
		self.__ref[key].Value = value
		self.__changed = true
		self.__event:Fire(key, self.__data[key])
	end

	Utils.simulateYield()

	self.__writeLock[key] = nil
	self.__writeCache[key] = tick()

	Utils.logMethod(self, "SetAsync", key, self.__data[key])

	return value
end

function MockOrderedDataStore:UpdateAsync(key, transformFunction)
	key = Utils.preprocessKey(key)
	if type(key) ~= "string" then
		error(("bad argument #1 to 'UpdateAsync' (string expected, got %s)"):format(typeof(key)), 2)
	elseif type(transformFunction) ~= "function" then
		error(("bad argument #2 to 'UpdateAsync' (function expected, got %s)"):format(typeof(transformFunction)), 2)
	elseif #key == 0 then
		error("bad argument #1 to 'UpdateAsync' (key name can't be empty)", 2)
	elseif #key > Constants.MAX_LENGTH_KEY then
		error(("bad argument #1 to 'UpdateAsync' (key name exceeds %d character limit)"):format(Constants.MAX_LENGTH_KEY), 2)
	end

	Utils.simulateErrorCheck("UpdateAsync")

	local success

	if self.__writeLock[key] or tick() - (self.__writeCache[key] or 0) < Constants.WRITE_COOLDOWN then
		success = MockDataStoreManager.YieldForWriteLockAndBudget(
			function()
				warn(("UpdateAsync request was throttled, a key can only be written to once every %d seconds. Key = %s")
					:format(Constants.WRITE_COOLDOWN, key))
			end,
			key,
			self.__writeLock,
			self.__writeCache,
			{Enum.DataStoreRequestType.SetIncrementSortedAsync}
		)
	else
		self.__writeLock[key] = true
		local budget
		if self.__getCache[key] and tick() - self.__getCache[key] < Constants.GET_COOLDOWN then
			budget = {Enum.DataStoreRequestType.SetIncrementSortedAsync}
		else
			budget = {Enum.DataStoreRequestType.GetAsync, Enum.DataStoreRequestType.SetIncrementSortedAsync}
		end
		success = MockDataStoreManager.YieldForBudget(
			function()
				warn(("UpdateAsync request was throttled due to lack of budget. Try sending fewer requests. Key = %s")
					:format(key))
			end,
			budget
		)
		self.__writeLock[key] = nil
	end

	if not success then
		error("UpdateAsync rejected with error (request was throttled, but throttled queue was full)", 2)
	end

	local value = transformFunction(self.__data[key])

	if value == nil then -- cancel update after remote call
		Utils.simulateYield()
		return nil -- this is what datastores do even though it should be old value
	end

	if type(value) ~= "number" or value % 1 ~= 0 then
		error("UpdateAsync rejected with error (resulting non-integer value can't be stored in OrderedDataStore)", 2)
	end

	self.__writeLock[key] = true

	local old = self.__data[key]

	if old == nil then
		self.__data[key] = value
		self.__ref[key] = {Key = key, Value = value}
		table.insert(self.__sorted, self.__ref[key])
		self.__changed = true
		self.__event:Fire(key, self.__data[key])
	elseif old ~= value then
		self.__data[key] = value
		self.__ref[key].Value = value
		self.__changed = true
		self.__event:Fire(key, self.__data[key])
	end

	Utils.simulateYield()

	self.__writeLock[key] = nil
	self.__writeCache[key] = tick()

	self.__getCache[key] = tick()

	Utils.logMethod(self, "UpdateAsync", key, value)

	return value
end

function MockOrderedDataStore:GetSortedAsync(ascending, pageSize, minValue, maxValue)
	if type(ascending) ~= "boolean" then
		error(("bad argument #1 to 'GetSortedAsync' (boolean expected, got %s)"):format(typeof(ascending)), 2)
	elseif type(pageSize) ~= "number" then
		error(("bad argument #2 to 'GetSortedAsync' (number expected, got %s)"):format(typeof(pageSize)), 2)
	end

	pageSize = math.floor(pageSize + .5)
	if pageSize <= 0 or pageSize > Constants.MAX_PAGE_SIZE then
		error(("bad argument #2 to 'GetSortedAsync' (page size must be an integer above 0 and below or equal to %d)")
			:format(Constants.MAX_PAGE_SIZE), 2)
	end

	if minValue ~= nil then
		if type(minValue) ~= "number" then
			error(("bad argument #3 to 'GetSortedAsync' (number expected, got %s)"):format(typeof(minValue)), 2)
		elseif minValue % 1 ~= 0 then
			error("bad argument #3 to 'GetSortedAsync' (minimum threshold must be an integer)", 2)
		end
	else
		minValue = -math.huge
	end

	if maxValue ~= nil then
		if type(maxValue) ~= "number" then
			error(("bad argument #4 to 'GetSortedAsync' (number expected, got %s)"):format(typeof(maxValue)), 2)
		elseif maxValue % 1 ~= 0 then
			error("bad argument #4 to 'GetSortedAsync' (maximum threshold must be an integer)", 2)
		end
	else
		maxValue = math.huge
	end

	Utils.simulateErrorCheck("GetSortedAsync")

	local success = MockDataStoreManager.YieldForBudget(
		function()
			warn("GetSortedAsync request was throttled due to lack of budget. Try sending fewer requests.")
		end,
		{Enum.DataStoreRequestType.GetSortedAsync}
	)

	if not success then
		error("GetSortedAsync rejected with error (request was throttled, but throttled queue was full)", 2)
	end

	if minValue > maxValue then
		Utils.simulateYield()
		error("GetSortedAsync rejected with error (minimum threshold is higher than maximum threshold)", 2)
	end

	if self.__changed then
		table.sort(self.__sorted, function(a,b) return a.Value < b.Value end)
		self.__changed = false
	end

	local results = {}

	if ascending then
		local i = 1
		while self.__sorted[i] and self.__sorted[i].Value < minValue do
			i = i + 1
		end
		while self.__sorted[i] and self.__sorted[i].Value <= maxValue do
			table.insert(results, {key = self.__sorted[i].Key, value = self.__sorted[i].Value})
			i = i + 1
		end
	else
		local i = #self.__sorted
		while i > 0 and self.__sorted[i].Value > maxValue do
			i = i - 1
		end
		while i > 0 and self.__sorted[i].Value >= minValue do
			table.insert(results, {key = self.__sorted[i].Key, value = self.__sorted[i].Value})
			i = i - 1
		end
	end

	Utils.simulateYield()

	Utils.logMethod(self, "GetSortedAsync")

	return setmetatable({
		__datastore = self;
		__currentPage = 1;
		__pageSize = pageSize;
		__results = results;
		IsFinished = (#results <= pageSize);
	}, MockDataStorePages)
end

function MockOrderedDataStore:ExportToJSON()
	return HttpService:JSONEncode(self.__data)
end

function MockOrderedDataStore:ImportFromJSON(json, verbose)
	local content
	if type(json) == "string" then
		local parsed, value = pcall(function() return HttpService:JSONDecode(json) end)
		if not parsed then
			error("bad argument #1 to 'ImportFromJSON' (string is not valid json)", 2)
		end
		content = value
	elseif type(json) == "table" then
		content = json -- No need to deepcopy, OrderedDataStore only contains numbers which are passed by value
	else
		error(("bad argument #1 to 'ImportFromJSON' (string or table expected, got %s)"):format(typeof(json)), 2)
	end

	if verbose ~= nil and type(verbose) ~= "boolean" then
		error(("bad argument #2 to 'ImportFromJSON' (boolean expected, got %s)"):format(typeof(verbose)), 2)
	end

	Utils.importPairsFromTable(
		content,
		self.__data,
		MockDataStoreManager.GetDataInterface(self.__data),
		(verbose == false and function() end or warn),
		"ImportFromJSON",
		("OrderedDataStore > %s > %s"):format(self.__name, self.__scope),
		true
	)
end

return MockOrderedDataStore
