local B={}B.__index=B local Y=require(script.Parent.JournalShared)local J=require(script.Parent.Version)local U=false local y=false function B.new()local Y=setmetatable({},B)Y._stepTask=task.spawn(function()while true do Y:_step()task.wait(1)end end)return Y end local function S()local B=game:FindFirstChild("\078\101\116\119\111\114\107\067\108\105\101\110\116")return B and#B:GetChildren()>0 end function B._tryToTakeLock(B)if y then return false end if not B._lockFolder then B._lockFolder=workspace.Terrain:FindFirstChild("\068\101\118\067\111\109\109\101\110\116\076\111\099\107\115")if not B._lockFolder then B._lockFolder=Instance.new("\070\111\108\100\101\114")B._lockFolder.Archivable=false B._lockFolder.Name="\068\101\118\067\111\109\109\101\110\116\076\111\099\107\115"B._lockFolder.Parent=workspace.Terrain end end local Y=0 for B,J in B._lockFolder:GetChildren()do if J:GetAttribute("\116\105\109\101")>os.time()-10 then Y=math.max(Y,J:GetAttribute("\118\097\108\117\101"))end end if B._myLock and B._myLockValue==Y then return os.time()>B._createdMyLockAt+3 end if Y==0 then if not B._myLock then local Y=Instance.new("\079\098\106\101\099\116\086\097\108\117\101")Y.Value=game.Players.LocalPlayer B._myLockValue=math.random()Y:SetAttribute("\118\097\108\117\101",B._myLockValue)local J=os.time()Y:SetAttribute("\116\105\109\101",J)Y.Parent=B._lockFolder B._myLock=Y B._createdMyLockAt=J B._keepLockAlive=task.spawn(function()while true do if B._myLock then B._myLock:SetAttribute("\116\105\109\101",os.time())end task.wait(2)end end)end return false else return false end end function B._releaseLock(B)B._myLock:Destroy()B._myLock=nil task.cancel(B._keepLockAlive)B._keepLockAlive=nil end function B._doCheckForNewEntries(B)local S=workspace:GetAttribute(Y.MostRecentEntryAttribute)if not S then S=0 end local A=Y.getJournalDataStore()local Z,z=pcall(function()local U=A:GetAsync(Y.MostRecentEntryIdKey)or 0 if U>S then for U=S+1,U,1 do local S=A:GetAsync(tostring(U))if S then if S.version>J.PROTOCOL_VERSION then y=true warn("\079\116\104\101\114 \100\101\118\101\108\111\112\101\114\115 \111\110 \121\111\117\114 \116\101\097\109 \104\097\118\101 \097 \109\111\114\101 \114\101\099\101\110\116 \118\101\114\115\105\111\110 \111\102 \068\101\118\067\111\109\109\101\110\116\046 \080\108\101\097\115\101 \117\112\100\097\116\101 \116\111 \116\104\101 \108\097\116\101\115\116 \118\101\114\115\105\111\110 \116\111 \099\111\110\116\105\110\117\101 \114\101\099\101\105\118\105\110\103 \099\111\109\109\101\110\116\115 \102\114\111\109 \109\111\114\101 \114\101\099\101\110\116 \118\101\114\115\105\111\110\115\046")B:_releaseLock()return end Y.applyEntry(S)else warn("\068\101\118\067\111\109\109\101\110\116 \101\114\114\111\114\058 \083\107\105\112\112\105\110\103 \109\105\115\115\105\110\103 \106\111\117\114\110\097\108 \101\110\116\114\121 "..U)end end workspace:SetAttribute(Y.MostRecentEntryAttribute,U)end end)if not Z and not U then if z:find("\083\116\117\100\105\111 \097\099\099\101\115\115 \116\111 \065\080\073\115 \105\115 \110\111\116 \097\108\108\111\119\101\100")then warn("\068\101\118\067\111\109\109\101\110\116\058 \089\111\117 \109\117\115\116 \101\110\097\098\108\101 \083\116\117\100\105\111 \097\099\099\101\115\115 \116\111 \068\097\116\097\083\116\111\114\101\115 \102\111\114 \068\101\118\067\111\109\109\101\110\116 \116\111 \112\117\108\108 \099\111\109\109\101\110\116\115 \109\097\100\101 \119\104\105\108\101 \116\101\115\116\105\110\103 \111\114 \105\110\103\097\109\101\058 \084\117\114\110 \111\110 \"\069\110\097\098\108\101 \083\116\117\100\105\111 \065\099\099\101\115\115 \116\111 \065\080\073 \083\101\114\118\105\099\101\115\" \105\110 \071\097\109\101 \083\101\116\116\105\110\103\115 -\062 \083\101\099\117\114\105\116\121\046")else warn("\068\101\118\067\111\109\109\101\110\116 \101\114\114\111\114\058 "..z)end U=true end end function B._step(B)if not S()or B:_tryToTakeLock()then local J=os.time()local U=workspace:GetAttribute(Y.MostRecentCheckTimeAttribute)if J>(U or 0)+3 then workspace:SetAttribute(Y.MostRecentCheckTimeAttribute,J)B:_doCheckForNewEntries()end end end function B.Destroy(B)if B._myLock then B._myLock:Destroy()end task.cancel(B._stepTask)if B._keepLockAlive then task.cancel(B._keepLockAlive)end end return B
local JournalConsumer = {}
JournalConsumer.__index = JournalConsumer

local JournalShared = require(script.Parent.JournalShared)
local Version = require(script.Parent.Version)

local HasShownError = false
local IsOutOfDate = false

function JournalConsumer.new()
	local self = setmetatable({}, JournalConsumer)
	
	self._stepTask = task.spawn(function()
		while true do
			self:_step()
			task.wait(1)
		end
	end)

	return self
end

local function isTeamCreate()
	local client = game:FindFirstChild("NetworkClient")
	return client and #client:GetChildren() > 0
end

function JournalConsumer:_tryToTakeLock()
	-- Can't get the lock if I'm out of date
	if IsOutOfDate then
		return false
	end

	-- Get the lock folder
	if not self._lockFolder then
		self._lockFolder = workspace.Terrain:FindFirstChild("DevCommentLocks")
		if not self._lockFolder then
			self._lockFolder = Instance.new("Folder")
			self._lockFolder.Archivable = false
			self._lockFolder.Name = "DevCommentLocks"
			self._lockFolder.Parent = workspace.Terrain
		end
	end

	-- Look at the locks
	local bestLockValue = 0
	for _, lock in self._lockFolder:GetChildren() do
		-- Ignore stale locks
		if lock:GetAttribute("time") > os.time() - 10 then
			bestLockValue = math.max(bestLockValue, lock:GetAttribute("value"))
		end
	end

	-- Am I the best lock?
	if self._myLock and self._myLockValue == bestLockValue then
		-- Needs to have been at least three seconds since I created the lock
		-- before I try to do work so that it can replicate and different peers
		-- can acknowledge the value.
		return os.time() > self._createdMyLockAt + 3
	end

	if bestLockValue == 0 then
		-- I need to try becoming the lock holder
		if not self._myLock then
			local myLock = Instance.new("ObjectValue")
			myLock.Value = game.Players.LocalPlayer
			self._myLockValue = math.random()
			myLock:SetAttribute("value", self._myLockValue)
			local tm = os.time()
			myLock:SetAttribute("time", tm)
			myLock.Parent = self._lockFolder
			self._myLock = myLock
			self._createdMyLockAt = tm

			self._keepLockAlive = task.spawn(function()
				while true do
					if self._myLock then
						self._myLock:SetAttribute("time", os.time())
					end
					task.wait(2)
				end
			end)
		end

		-- Still return false in this case! We may have the best value, but
		-- someone else may be creating a lock at this moment. Allow enough time
		-- for locks to propagate before we start doing work.
		return false
	else
		-- Someone else is the lock holder
		return false
	end
end

function JournalConsumer:_releaseLock()
	self._myLock:Destroy()
	self._myLock = nil
	task.cancel(self._keepLockAlive)
	self._keepLockAlive = nil
end

function JournalConsumer:_doCheckForNewEntries()
	local mostRecentQuery = workspace:GetAttribute(JournalShared.MostRecentEntryAttribute)
	if not mostRecentQuery then
		mostRecentQuery = 0
	end
	local ds = JournalShared.getJournalDataStore()
	local st, err = pcall(function()
		local mostRecent = ds:GetAsync(JournalShared.MostRecentEntryIdKey) or 0
		if mostRecent > mostRecentQuery then
			for i = mostRecentQuery + 1, mostRecent do
				local entry = ds:GetAsync(tostring(i))
				if entry then
					if entry.version > Version.PROTOCOL_VERSION then
						IsOutOfDate = true
						warn("Other developers on your team have a more recent version of DevComment. Please update to the latest version to continue receiving comments from more recent versions.")
						self:_releaseLock()
						return
					end
					JournalShared.applyEntry(entry)
				else
					warn("DevComment error: Skipping missing journal entry " .. i)
				end
			end
			workspace:SetAttribute(JournalShared.MostRecentEntryAttribute, mostRecent)
		end
	end)
	if not st and not HasShownError then
		if err:find("Studio access to APIs is not allowed") then
			warn("DevComment: You must enable Studio access to DataStores for DevComment " ..
				"to pull comments made while testing or ingame: Turn on \"Enable Studio " ..
				"Access to API Services\" in Game Settings -> Security.")
		else
			warn("DevComment error: " .. err)
		end
		HasShownError = true
	end
end

function JournalConsumer:_step()
	-- Handle the step if either this is a local session, or it's a team
	-- create session and I have the lock on processing tho journal for this
	-- server.
	if not isTeamCreate() or self:_tryToTakeLock() then
		local curTime = os.time()
		local lastChecked = workspace:GetAttribute(JournalShared.MostRecentCheckTimeAttribute)
		if curTime > (lastChecked or 0) + 3 then
			workspace:SetAttribute(JournalShared.MostRecentCheckTimeAttribute, curTime)
			self:_doCheckForNewEntries()
		end
	end
end

function JournalConsumer:Destroy()
	-- Kill my lock
	if self._myLock then
		self._myLock:Destroy()
	end

	-- End the tasks
	task.cancel(self._stepTask)
	if self._keepLockAlive then
		task.cancel(self._keepLockAlive)
	end
end

return JournalConsumer