local H=game:GetService("\072\116\116\112\083\101\114\118\105\099\101")local k=require(script.Parent.MockDataStoreConstants)local F=require(script.Parent.MockDataStoreManager)local u=require(script.Parent.MockDataStoreUtils)local v={}v.__index=v local Z=Random.new()function v.OnUpdate(O,H,v)H=u.preprocessKey(H)if type(H)~="\115\116\114\105\110\103"then error(string.format("\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",typeof(H)),2)elseif type(v)~="\102\117\110\099\116\105\111\110"then error(string.format("\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",typeof(v)),2)elseif#H==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#H>k.MAX_LENGTH_KEY then error(string.format("\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",k.MAX_LENGTH_KEY),2)end u.simulateErrorCheck("\079\110\085\112\100\097\116\101")local L=F.YieldForBudget(function()warn(string.format("\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",H))end,table.create(1,Enum.DataStoreRequestType.OnUpdate))if not L 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 u.logMethod(O,"\079\110\085\112\100\097\116\101",H)return O.__event.Event:Connect(function(F,O)if F==H then if k.YIELD_TIME_UPDATE_MAX>0 then u.accurateWait(Z:NextNumber(k.YIELD_TIME_UPDATE_MIN,k.YIELD_TIME_UPDATE_MAX))end v(O)end end)end function v.GetAsync(v,H)H=u.preprocessKey(H)if type(H)~="\115\116\114\105\110\103"then error(string.format("\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",typeof(H)),2)elseif#H==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#H>k.MAX_LENGTH_KEY then error(string.format("\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",k.MAX_LENGTH_KEY),2)end if v.__getCache[H]and tick()-v.__getCache[H]<k.GET_COOLDOWN then return u.deepcopy(v.__data[H])end u.simulateErrorCheck("\071\101\116\065\115\121\110\099")local Z=F.YieldForBudget(function()warn(string.format("\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",H))end,table.create(1,Enum.DataStoreRequestType.GetAsync))if not Z 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 v.__getCache[H]=tick()local O=u.deepcopy(v.__data[H])u.simulateYield()u.logMethod(v,"\071\101\116\065\115\121\110\099",H)return O end function v.IncrementAsync(Z,H,v)H=u.preprocessKey(H)if type(H)~="\115\116\114\105\110\103"then error(string.format("\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",typeof(H)),2)elseif v~=nil and type(v)~="\110\117\109\098\101\114"then error(string.format("\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",typeof(v)),2)elseif#H==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#H>k.MAX_LENGTH_KEY then error(string.format("\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",k.MAX_LENGTH_KEY),2)end u.simulateErrorCheck("\073\110\099\114\101\109\101\110\116\065\115\121\110\099")local O if Z.__writeLock[H]or tick()-(Z.__writeCache[H]or 0)<k.WRITE_COOLDOWN then O=F.YieldForWriteLockAndBudget(function()warn(string.format("\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",k.WRITE_COOLDOWN,H))end,H,Z.__writeLock,Z.__writeCache,table.create(1,Enum.DataStoreRequestType.SetIncrementAsync))else Z.__writeLock[H]=true O=F.YieldForBudget(function()warn(string.format("\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",H))end,table.create(1,Enum.DataStoreRequestType.SetIncrementAsync))Z.__writeLock[H]=nil end if not O 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 L=Z.__data[H]if L~=nil and(type(L)~="\110\117\109\098\101\114"or L%1~=0)then u.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 Z.__writeLock[H]=true v=v and math.floor(v+.5)or 1 Z.__data[H]=(L or 0)+v if L==nil or v~=0 then Z.__event:Fire(H,Z.__data[H])end local X=Z.__data[H]u.simulateYield()Z.__writeLock[H]=nil Z.__writeCache[H]=tick()Z.__getCache[H]=tick()u.logMethod(Z,"\073\110\099\114\101\109\101\110\116\065\115\121\110\099",H,X,v)return X end function v.RemoveAsync(v,H)H=u.preprocessKey(H)if type(H)~="\115\116\114\105\110\103"then error(string.format("\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",typeof(H)),2)elseif#H==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#H>k.MAX_LENGTH_KEY then error(string.format("\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",k.MAX_LENGTH_KEY),2)end u.simulateErrorCheck("\082\101\109\111\118\101\065\115\121\110\099")local Z if v.__writeLock[H]or tick()-(v.__writeCache[H]or 0)<k.WRITE_COOLDOWN then Z=F.YieldForWriteLockAndBudget(function()warn(string.format("\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",k.WRITE_COOLDOWN,H))end,H,v.__writeLock,v.__writeCache,table.create(1,Enum.DataStoreRequestType.SetIncrementAsync))else v.__writeLock[H]=true Z=F.YieldForBudget(function()warn(string.format("\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",H))end,table.create(1,Enum.DataStoreRequestType.SetIncrementAsync))v.__writeLock[H]=nil end if not Z 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 v.__writeLock[H]=true local O=u.deepcopy(v.__data[H])v.__data[H]=nil if O~=nil then v.__event:Fire(H,nil)end u.simulateYield()v.__writeLock[H]=nil v.__writeCache[H]=tick()u.logMethod(v,"\082\101\109\111\118\101\065\115\121\110\099",H,O)return O end local function O(k)return H:JSONEncode(k)end local function L(k)return H:JSONDecode(k)end function v.SetAsync(Z,H,v)H=u.preprocessKey(H)if type(H)~="\115\116\114\105\110\103"then error(string.format("\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",typeof(H)),2)elseif#H==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#H>k.MAX_LENGTH_KEY then error(string.format("\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",k.MAX_LENGTH_KEY),2)elseif v==nil or type(v)=="\102\117\110\099\116\105\111\110"or type(v)=="\117\115\101\114\100\097\116\097"or type(v)=="\116\104\114\101\097\100"then error(string.format("\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",tostring(v),typeof(v)),2)end if type(v)=="\116\097\098\108\101"then local H,F,Z=u.scanValidity(v)if not H then error(string.format("\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",u.getStringPath(F),Z),2)end local L,X=pcall(O,v)if not L 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#X>k.MAX_LENGTH_DATA then error(string.format("\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",k.MAX_LENGTH_DATA),2)end elseif type(v)=="\115\116\114\105\110\103"then if#v>k.MAX_LENGTH_DATA then error(string.format("\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",k.MAX_LENGTH_DATA),2)elseif not utf8.len(v)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 u.simulateErrorCheck("\083\101\116\065\115\121\110\099")local L if Z.__writeLock[H]or tick()-(Z.__writeCache[H]or 0)<k.WRITE_COOLDOWN then L=F.YieldForWriteLockAndBudget(function()warn(string.format("\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",k.WRITE_COOLDOWN,H))end,H,Z.__writeLock,Z.__writeCache,table.create(1,Enum.DataStoreRequestType.SetIncrementAsync))else Z.__writeLock[H]=true L=F.YieldForBudget(function()warn(string.format("\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",H))end,table.create(1,Enum.DataStoreRequestType.SetIncrementAsync))Z.__writeLock[H]=nil end if not L 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 Z.__writeLock[H]=true if type(v)=="\116\097\098\108\101"or v~=Z.__data[H]then Z.__data[H]=u.deepcopy(v)Z.__event:Fire(H,Z.__data[H])end u.simulateYield()Z.__writeLock[H]=nil Z.__writeCache[H]=tick()u.logMethod(Z,"\083\101\116\065\115\121\110\099",H,Z.__data[H])end function v.UpdateAsync(Z,H,v)H=u.preprocessKey(H)if type(H)~="\115\116\114\105\110\103"then error(string.format("\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",typeof(H)),2)elseif type(v)~="\102\117\110\099\116\105\111\110"then error(string.format("\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",typeof(v)),2)elseif#H==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#H>k.MAX_LENGTH_KEY then error(string.format("\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",k.MAX_LENGTH_KEY),2)end u.simulateErrorCheck("\085\112\100\097\116\101\065\115\121\110\099")local L if Z.__writeLock[H]or tick()-(Z.__writeCache[H]or 0)<k.WRITE_COOLDOWN then L=F.YieldForWriteLockAndBudget(function()warn(string.format("\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",k.WRITE_COOLDOWN,H))end,H,Z.__writeLock,Z.__writeCache,table.create(1,Enum.DataStoreRequestType.SetIncrementAsync))else Z.__writeLock[H]=true local u if Z.__getCache[H]and tick()-Z.__getCache[H]<k.GET_COOLDOWN then u=table.create(1,Enum.DataStoreRequestType.SetIncrementAsync)else u={Enum.DataStoreRequestType.GetAsync;Enum.DataStoreRequestType.SetIncrementAsync}end L=F.YieldForBudget(function()warn(string.format("\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",H))end,u)Z.__writeLock[H]=nil end if not L 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=v(u.deepcopy(Z.__data[H]))if X==nil then u.simulateYield()return nil end if type(X)=="\102\117\110\099\116\105\111\110"or type(X)=="\117\115\101\114\100\097\116\097"or type(X)=="\116\104\114\101\097\100"then error(string.format("\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",tostring(X),typeof(X)),2)end if type(X)=="\116\097\098\108\101"then local H,F,v=u.scanValidity(X)if not H then error(string.format("\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",u.getStringPath(F),v),2)end local Z,L=pcall(O,X)if not 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\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#L>k.MAX_LENGTH_DATA then error(string.format("\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",k.MAX_LENGTH_DATA),2)end elseif type(X)=="\115\116\114\105\110\103"then if#X>k.MAX_LENGTH_DATA then error(string.format("\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",k.MAX_LENGTH_DATA),2)elseif not utf8.len(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\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 Z.__writeLock[H]=true if type(X)=="\116\097\098\108\101"or X~=Z.__data[H]then Z.__data[H]=u.deepcopy(X)Z.__event:Fire(H,Z.__data[H])end local t=u.deepcopy(X)Z.__writeLock[H]=nil Z.__writeCache[H]=tick()Z.__getCache[H]=tick()u.logMethod(Z,"\085\112\100\097\116\101\065\115\121\110\099",H,t)return t end function v.ExportToJSON(k)return H:JSONEncode(k.__data)end function v.ImportFromJSON(v,H,k)local Z if type(H)=="\115\116\114\105\110\103"then local k,F=pcall(L,H)if not k 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 Z=F elseif type(H)=="\116\097\098\108\101"then Z=u.deepcopy(H)else error(string.format("\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",typeof(H)),2)end if k~=nil and type(k)~="\098\111\111\108\101\097\110"then error(string.format("\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",typeof(k)),2)end u.importPairsFromTable(Z,v.__data,F.GetDataInterface(v.__data),k==false and function() end or warn,"\073\109\112\111\114\116\070\114\111\109\074\083\079\078",(type(v.__name)=="\115\116\114\105\110\103"and type(v.__scope)=="\115\116\114\105\110\103")and string.format("\068\097\116\097\083\116\111\114\101 \062 \037\115 \062 \037\115",v.__name,v.__scope)or"\071\108\111\098\097\108\068\097\116\097\083\116\111\114\101",false)end return v
--[[
	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:
	https://github.com/buildthomas/MockDataStoreService/blob/master/LICENSE
]]

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

local MockGlobalDataStore = {}
MockGlobalDataStore.__index = MockGlobalDataStore

local rand = Random.new()

function MockGlobalDataStore:OnUpdate(key, callback)
	key = Utils.preprocessKey(key)
	if type(key) ~= "string" then
		error(string.format("bad argument #1 to 'OnUpdate' (string expected, got %s)", typeof(key)), 2)
	elseif type(callback) ~= "function" then
		error(string.format("bad argument #2 to 'OnUpdate' (function expected, got %s)", 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(
			string.format(
				"bad argument #1 to 'OnUpdate' (key name exceeds %d character limit)",
				Constants.MAX_LENGTH_KEY
			),
			2
		)
	end

	Utils.simulateErrorCheck("OnUpdate")
	local success = MockDataStoreManager.YieldForBudget(function()
		warn(
			string.format(
				"OnUpdate request was throttled due to lack of budget. Try sending fewer requests. Key = %s",
				key
			)
		)
	end, table.create(1, 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
				Utils.accurateWait(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(string.format("bad argument #1 to 'GetAsync' (string expected, got %s)", 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(
			string.format(
				"bad argument #1 to 'GetAsync' (key name exceeds %d character limit)",
				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(
			string.format(
				"GetAsync request was throttled due to lack of budget. Try sending fewer requests. Key = %s",
				key
			)
		)
	end, table.create(1, 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(string.format("bad argument #1 to 'IncrementAsync' (string expected, got %s)", typeof(key)), 2)
	elseif delta ~= nil and type(delta) ~= "number" then
		error(string.format("bad argument #2 to 'IncrementAsync' (number expected, got %s)", 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(
			string.format(
				"bad argument #1 to 'IncrementAsync' (key name exceeds %d character limit)",
				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(
				string.format(
					"IncrementAsync request was throttled, a key can only be written to once every %d seconds. Key = %s",
					Constants.WRITE_COOLDOWN,
					key
				)
			)
		end, key, self.__writeLock, self.__writeCache, table.create(1, Enum.DataStoreRequestType.SetIncrementAsync))
	else
		self.__writeLock[key] = true
		success = MockDataStoreManager.YieldForBudget(function()
			warn(
				string.format(
					"IncrementAsync request was throttled due to lack of budget. Try sending fewer requests. Key = %s",
					key
				)
			)
		end, table.create(1, 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 + 0.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(string.format("bad argument #1 to 'RemoveAsync' (string expected, got %s)", 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(
			string.format(
				"bad argument #1 to 'RemoveAsync' (key name exceeds %d character limit)",
				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(
				string.format(
					"RemoveAsync request was throttled, a key can only be written to once every %d seconds. Key = %s",
					Constants.WRITE_COOLDOWN,
					key
				)
			)
		end, key, self.__writeLock, self.__writeCache, table.create(1, Enum.DataStoreRequestType.SetIncrementAsync))
	else
		self.__writeLock[key] = true
		success = MockDataStoreManager.YieldForBudget(function()
			warn(
				string.format(
					"RemoveAsync request was throttled due to lack of budget. Try sending fewer requests. Key = %s",
					key
				)
			)
		end, table.create(1, 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

local function JsonEncode(Data)
	return HttpService:JSONEncode(Data)
end

local function JsonDecode(JsonString)
	return HttpService:JSONDecode(JsonString)
end

function MockGlobalDataStore:SetAsync(key, value)
	key = Utils.preprocessKey(key)
	if type(key) ~= "string" then
		error(string.format("bad argument #1 to 'SetAsync' (string expected, got %s)", 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(
			string.format(
				"bad argument #1 to 'SetAsync' (key name exceeds %d character limit)",
				Constants.MAX_LENGTH_KEY
			),
			2
		)
	elseif value == nil or type(value) == "function" or type(value) == "userdata" or type(value) == "thread" then
		error(
			string.format(
				"bad argument #2 to 'SetAsync' (cannot store value '%s' of type %s)",
				tostring(value),
				typeof(value)
			),
			2
		)
	end

	if type(value) == "table" then
		local isValid, keyPath, reason = Utils.scanValidity(value)
		if not isValid then
			error(
				string.format(
					"bad argument #2 to 'SetAsync' (table has invalid entry at <%s>: %s)",
					Utils.getStringPath(keyPath),
					reason
				),
				2
			)
		end

		local pass, content = pcall(JsonEncode, value)
		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(
				string.format(
					"bad argument #2 to 'SetAsync' (encoded data length exceeds %d character limit)",
					Constants.MAX_LENGTH_DATA
				),
				2
			)
		end
	elseif type(value) == "string" then
		if #value > Constants.MAX_LENGTH_DATA then
			error(
				string.format(
					"bad argument #2 to 'SetAsync' (data length exceeds %d character limit)",
					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(
				string.format(
					"SetAsync request was throttled, a key can only be written to once every %d seconds. Key = %s",
					Constants.WRITE_COOLDOWN,
					key
				)
			)
		end, key, self.__writeLock, self.__writeCache, table.create(1, Enum.DataStoreRequestType.SetIncrementAsync))
	else
		self.__writeLock[key] = true
		success = MockDataStoreManager.YieldForBudget(function()
			warn(
				string.format(
					"SetAsync request was throttled due to lack of budget. Try sending fewer requests. Key = %s",
					key
				)
			)
		end, table.create(1, 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(string.format("bad argument #1 to 'UpdateAsync' (string expected, got %s)", typeof(key)), 2)
	elseif type(transformFunction) ~= "function" then
		error(
			string.format("bad argument #2 to 'UpdateAsync' (function expected, got %s)", 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(
			string.format(
				"bad argument #1 to 'UpdateAsync' (key name exceeds %d character limit)",
				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(
				string.format(
					"UpdateAsync request was throttled, a key can only be written to once every %d seconds. Key = %s",
					Constants.WRITE_COOLDOWN,
					key
				)
			)
		end, key, self.__writeLock, self.__writeCache, table.create(1, Enum.DataStoreRequestType.SetIncrementAsync))
	else
		self.__writeLock[key] = true
		local budget
		if self.__getCache[key] and tick() - self.__getCache[key] < Constants.GET_COOLDOWN then
			budget = table.create(1, Enum.DataStoreRequestType.SetIncrementAsync)
		else
			budget = {Enum.DataStoreRequestType.GetAsync, Enum.DataStoreRequestType.SetIncrementAsync}
		end

		success = MockDataStoreManager.YieldForBudget(function()
			warn(
				string.format(
					"UpdateAsync request was throttled due to lack of budget. Try sending fewer requests. Key = %s",
					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(
			string.format(
				"UpdateAsync rejected with error (resulting value '%s' is of type %s that cannot be stored)",
				tostring(value),
				typeof(value)
			),
			2
		)
	end

	if type(value) == "table" then
		local isValid, keyPath, reason = Utils.scanValidity(value)
		if not isValid then
			error(
				string.format(
					"UpdateAsync rejected with error (resulting table has invalid entry at <%s>: %s)",
					Utils.getStringPath(keyPath),
					reason
				),
				2
			)
		end

		local pass, content = pcall(JsonEncode, value)
		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(
				string.format(
					"UpdateAsync rejected with error (resulting encoded data length exceeds %d character limit)",
					Constants.MAX_LENGTH_DATA
				),
				2
			)
		end
	elseif type(value) == "string" then
		if #value > Constants.MAX_LENGTH_DATA then
			error(
				string.format(
					"UpdateAsync rejected with error (resulting data length exceeds %d character limit)",
					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(JsonDecode, json)
		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(string.format("bad argument #1 to 'ImportFromJSON' (string or table expected, got %s)", typeof(json)), 2)
	end

	if verbose ~= nil and type(verbose) ~= "boolean" then
		error(string.format("bad argument #2 to 'ImportFromJSON' (boolean expected, got %s)", 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 string.format("DataStore > %s > %s", self.__name, self.__scope)
			or "GlobalDataStore"
		),
		false
	)
end

return MockGlobalDataStore
