local O={}O.__index=O local g=require(script.Parent.MockDataStoreManager)local Y=require(script.Parent.MockDataStoreUtils)local M=require(script.Parent.MockDataStoreConstants)local D=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local k=Random.new()function O.OnUpdate(h,O,D)O=Y.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(D)~="\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(D)),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>M.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(M.MAX_LENGTH_KEY),2)end Y.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 Y.logMethod(h,"\079\110\085\112\100\097\116\101",O)return h.__event.Event:Connect(function(g,Y)if g==O then if M.YIELD_TIME_UPDATE_MAX>0 then task.wait(k:NextNumber(M.YIELD_TIME_UPDATE_MIN,M.YIELD_TIME_UPDATE_MAX))end D(Y)end end)end function O.GetAsync(D,O)O=Y.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>M.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(M.MAX_LENGTH_KEY),2)end if D.__getCache[O]and tick()-D.__getCache[O]<M.GET_COOLDOWN then return Y.deepcopy(D.__data[O])end Y.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 D.__getCache[O]=tick()local h=Y.deepcopy(D.__data[O])Y.simulateYield()Y.logMethod(D,"\071\101\116\065\115\121\110\099",O)return h end function O.IncrementAsync(k,O,D)O=Y.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 D~=nil and type(D)~="\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(D)),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>M.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(M.MAX_LENGTH_KEY),2)end Y.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)<M.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(M.WRITE_COOLDOWN,O))end,O,k.__writeLock,k.__writeCache,{Enum.DataStoreRequestType.SetIncrementAsync})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.SetIncrementAsync})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 Y.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 D=D and math.floor(D+.5)or 1 k.__data[O]=(X or 0)+D if X==nil or D~=0 then k.__event:Fire(O,k.__data[O])end local z=k.__data[O]Y.simulateYield()k.__writeLock[O]=nil k.__writeCache[O]=tick()k.__getCache[O]=tick()Y.logMethod(k,"\073\110\099\114\101\109\101\110\116\065\115\121\110\099",O,z,D)return z end function O.RemoveAsync(D,O)O=Y.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(typeof(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>M.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(M.MAX_LENGTH_KEY),2)end Y.simulateErrorCheck("\082\101\109\111\118\101\065\115\121\110\099")local k if D.__writeLock[O]or tick()-(D.__writeCache[O]or 0)<M.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(M.WRITE_COOLDOWN,O))end,O,D.__writeLock,D.__writeCache,{Enum.DataStoreRequestType.SetIncrementAsync})else D.__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.SetIncrementAsync})D.__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 D.__writeLock[O]=true local h=Y.deepcopy(D.__data[O])D.__data[O]=nil if h~=nil then D.__event:Fire(O,nil)end Y.simulateYield()D.__writeLock[O]=nil D.__writeCache[O]=tick()Y.logMethod(D,"\082\101\109\111\118\101\065\115\121\110\099",O,h)return h end function O.SetAsync(h,O,k)O=Y.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>M.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(M.MAX_LENGTH_KEY),2)elseif k==nil or type(k)=="\102\117\110\099\116\105\111\110"or type(k)=="\117\115\101\114\100\097\116\097"or type(k)=="\116\104\114\101\097\100"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 \118\097\108\117\101 \'\037\115\' \111\102 \116\121\112\101 \037\115\041"):format(tostring(k),typeof(k)),2)end if type(k)=="\116\097\098\108\101"then local O,g,h=Y.scanValidity(k)if not O 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\116\097\098\108\101 \104\097\115 \105\110\118\097\108\105\100 \101\110\116\114\121 \097\116 \060\037\115\062\058 \037\115\041"):format(Y.getStringPath(g),h),2)end local X,z=pcall(function()return D:JSONEncode(k)end)if not X 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\116\097\098\108\101 \099\111\117\108\100 \110\111\116 \098\101 \101\110\099\111\100\101\100 \116\111 \106\115\111\110\041",2)elseif#z>M.MAX_LENGTH_DATA 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\101\110\099\111\100\101\100 \100\097\116\097 \108\101\110\103\116\104 \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(M.MAX_LENGTH_DATA),2)end elseif type(k)=="\115\116\114\105\110\103"then if#k>M.MAX_LENGTH_DATA 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\100\097\116\097 \108\101\110\103\116\104 \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(M.MAX_LENGTH_DATA),2)elseif not utf8.len(k)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\115\116\114\105\110\103 \118\097\108\117\101 \105\115 \110\111\116 \118\097\108\105\100 \085\084\070-\056\041",2)end end Y.simulateErrorCheck("\083\101\116\065\115\121\110\099")local X if h.__writeLock[O]or tick()-(h.__writeCache[O]or 0)<M.WRITE_COOLDOWN then X=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(M.WRITE_COOLDOWN,O))end,O,h.__writeLock,h.__writeCache,{Enum.DataStoreRequestType.SetIncrementAsync})else h.__writeLock[O]=true X=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.SetIncrementAsync})h.__writeLock[O]=nil end if not X 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 h.__writeLock[O]=true if type(k)=="\116\097\098\108\101"or k~=h.__data[O]then h.__data[O]=Y.deepcopy(k)h.__event:Fire(O,h.__data[O])end Y.simulateYield()h.__writeLock[O]=nil h.__writeCache[O]=tick()Y.logMethod(h,"\083\101\116\065\115\121\110\099",O,h.__data[O])end function O.UpdateAsync(h,O,k)O=Y.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(k)~="\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(k)),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>M.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(M.MAX_LENGTH_KEY),2)end Y.simulateErrorCheck("\085\112\100\097\116\101\065\115\121\110\099")local X if h.__writeLock[O]or tick()-(h.__writeCache[O]or 0)<M.WRITE_COOLDOWN then X=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(M.WRITE_COOLDOWN,O))end,O,h.__writeLock,h.__writeCache,{Enum.DataStoreRequestType.SetIncrementAsync})else h.__writeLock[O]=true local Y if h.__getCache[O]and tick()-h.__getCache[O]<M.GET_COOLDOWN then Y={Enum.DataStoreRequestType.SetIncrementAsync}else Y={Enum.DataStoreRequestType.GetAsync;Enum.DataStoreRequestType.SetIncrementAsync}end X=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)h.__writeLock[O]=nil end if not X 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 z=k(Y.deepcopy(h.__data[O]))if z==nil then Y.simulateYield()return nil end if type(z)=="\102\117\110\099\116\105\111\110"or type(z)=="\117\115\101\114\100\097\116\097"or type(z)=="\116\104\114\101\097\100"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 \118\097\108\117\101 \'\037\115\' \105\115 \111\102 \116\121\112\101 \037\115 \116\104\097\116 \099\097\110\110\111\116 \098\101 \115\116\111\114\101\100\041"):format(tostring(z),typeof(z)),2)end if type(z)=="\116\097\098\108\101"then local O,g,k=Y.scanValidity(z)if not O 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 \116\097\098\108\101 \104\097\115 \105\110\118\097\108\105\100 \101\110\116\114\121 \097\116 \060\037\115\062\058 \037\115\041"):format(Y.getStringPath(g),k),2)end local h,X=pcall(function()return D:JSONEncode(z)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\115\117\108\116\105\110\103 \116\097\098\108\101 \099\111\117\108\100 \110\111\116 \098\101 \101\110\099\111\100\101\100 \116\111 \106\115\111\110\041",2)elseif#X>M.MAX_LENGTH_DATA 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 \101\110\099\111\100\101\100 \100\097\116\097 \108\101\110\103\116\104 \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(M.MAX_LENGTH_DATA),2)end elseif type(z)=="\115\116\114\105\110\103"then if#z>M.MAX_LENGTH_DATA 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 \100\097\116\097 \108\101\110\103\116\104 \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(M.MAX_LENGTH_DATA),2)elseif not utf8.len(z)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\115\116\114\105\110\103 \118\097\108\117\101 \105\115 \110\111\116 \118\097\108\105\100 \085\084\070-\056\041",2)end end h.__writeLock[O]=true if type(z)=="\116\097\098\108\101"or z~=h.__data[O]then h.__data[O]=Y.deepcopy(z)h.__event:Fire(O,h.__data[O])end local l=Y.deepcopy(z)h.__writeLock[O]=nil h.__writeCache[O]=tick()h.__getCache[O]=tick()Y.logMethod(h,"\085\112\100\097\116\101\065\115\121\110\099",O,l)return l end function O.ExportToJSON(O)return D:JSONEncode(O.__data)end function O.ImportFromJSON(k,O,M)local h if type(O)=="\115\116\114\105\110\103"then local g,Y=pcall(function()return D: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=Y.deepcopy(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 M~=nil and type(M)~="\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(M)),2)end Y.importPairsFromTable(h,k.__data,g.GetDataInterface(k.__data),M==false and function() end or warn,"\073\109\112\111\114\116\070\114\111\109\074\083\079\078",(type(k.__name)=="\115\116\114\105\110\103"and type(k.__scope)=="\115\116\114\105\110\103")and("\068\097\116\097\083\116\111\114\101 \062 \037\115 \062 \037\115"):format(k.__name,k.__scope)or"\071\108\111\098\097\108\068\097\116\097\083\116\111\114\101",false)end return O
--[[
	MockGlobalDataStore.lua
	This module implements the API and functionality of Roblox's GlobalDataStore class.

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

local MockGlobalDataStore = {}
MockGlobalDataStore.__index = MockGlobalDataStore

local MockDataStoreManager = require(script.Parent.MockDataStoreManager)
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 MockGlobalDataStore: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 MockGlobalDataStore: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 Utils.deepcopy(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

	self.__getCache[key] = tick()

	local retValue = Utils.deepcopy(self.__data[key])

	Utils.simulateYield()

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

	return retValue
end

function MockGlobalDataStore: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.SetIncrementAsync}
		)
	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.SetIncrementAsync}
		)
		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

	self.__data[key] = (old or 0) + delta

	if old == nil or delta ~= 0 then
		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 MockGlobalDataStore:RemoveAsync(key)
	key = Utils.preprocessKey(key)
	if type(key) ~= "string" then
		error(("bad argument #1 to 'RemoveAsync' (string expected, got %s)"):format(typeof(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.SetIncrementAsync}
		)
	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.SetIncrementAsync}
		)
		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 = Utils.deepcopy(self.__data[key])
	self.__data[key] = nil

	if value ~= nil then
		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 MockGlobalDataStore: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 value == nil or type(value) == "function" or type(value) == "userdata" or type(value) == "thread" then
		error(("bad argument #2 to 'SetAsync' (cannot store value '%s' of type %s)")
			:format(tostring(value), typeof(value)), 2)
	end

	if type(value) == "table" then
		local isValid, keyPath, reason = Utils.scanValidity(value)
		if not isValid then
			error(("bad argument #2 to 'SetAsync' (table has invalid entry at <%s>: %s)")
				:format(Utils.getStringPath(keyPath), reason), 2)
		end
		local pass, content = pcall(function() return HttpService:JSONEncode(value) end)
		if not pass then
			error("bad argument #2 to 'SetAsync' (table could not be encoded to json)", 2)
		elseif #content > Constants.MAX_LENGTH_DATA then
			error(("bad argument #2 to 'SetAsync' (encoded data length exceeds %d character limit)")
				:format(Constants.MAX_LENGTH_DATA), 2)
		end
	elseif type(value) == "string" then
		if #value > Constants.MAX_LENGTH_DATA then
			error(("bad argument #2 to 'SetAsync' (data length exceeds %d character limit)")
				:format(Constants.MAX_LENGTH_DATA), 2)
		elseif not utf8.len(value) then
			error("bad argument #2 to 'SetAsync' (string value is not valid UTF-8)", 2)
		end
	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.SetIncrementAsync}
		)
	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.SetIncrementAsync}
		)
		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

	if type(value) == "table" or value ~= self.__data[key] then
		self.__data[key] = Utils.deepcopy(value)
		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])

end

function MockGlobalDataStore: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.SetIncrementAsync}
		)
	else
		self.__writeLock[key] = true
		local budget
		if self.__getCache[key] and tick() - self.__getCache[key] < Constants.GET_COOLDOWN then
			budget = {Enum.DataStoreRequestType.SetIncrementAsync}
		else
			budget = {Enum.DataStoreRequestType.GetAsync, Enum.DataStoreRequestType.SetIncrementAsync}
		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(Utils.deepcopy(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) == "function" or type(value) == "userdata" or type(value) == "thread" then
		error(("UpdateAsync rejected with error (resulting value '%s' is of type %s that cannot be stored)")
			:format(tostring(value), typeof(value)), 2)
	end

	if type(value) == "table" then
		local isValid, keyPath, reason = Utils.scanValidity(value)
		if not isValid then
			error(("UpdateAsync rejected with error (resulting table has invalid entry at <%s>: %s)")
				:format(Utils.getStringPath(keyPath), reason), 2)
		end
		local pass, content = pcall(function() return HttpService:JSONEncode(value) end)
		if not pass then
			error("UpdateAsync rejected with error (resulting table could not be encoded to json)", 2)
		elseif #content > Constants.MAX_LENGTH_DATA then
			error(("UpdateAsync rejected with error (resulting encoded data length exceeds %d character limit)")
				:format(Constants.MAX_LENGTH_DATA), 2)
		end
	elseif type(value) == "string" then
		if #value > Constants.MAX_LENGTH_DATA then
			error(("UpdateAsync rejected with error (resulting data length exceeds %d character limit)")
				:format(Constants.MAX_LENGTH_DATA), 2)
		elseif not utf8.len(value) then
			error("UpdateAsync rejected with error (string value is not valid UTF-8)", 2)
		end
	end

	self.__writeLock[key] = true

	if type(value) == "table" or value ~= self.__data[key] then
		self.__data[key] = Utils.deepcopy(value)
		self.__event:Fire(key, self.__data[key])
	end

	local retValue = Utils.deepcopy(value)

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

	self.__getCache[key] = tick()

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

	return retValue
end

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

function MockGlobalDataStore: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 = Utils.deepcopy(json)
	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",
		((type(self.__name) == "string" and type(self.__scope) == "string")
			and ("DataStore > %s > %s"):format(self.__name, self.__scope)
			or "GlobalDataStore"),
		false
	)
end

return MockGlobalDataStore
