local v=game:GetService("\082\117\110\083\101\114\118\105\099\101")local m={}m.__index=m m.ClassName="\077\097\115\116\101\114\067\108\111\099\107"function m.new(v,u)local q=setmetatable({},m)q._remoteEvent=v or error("\078\111 \114\101\109\111\116\101\069\118\101\110\116")q._remoteFunction=u or error("\078\111 \114\101\109\111\116\101\070\117\110\099\116\105\111\110")q._remoteFunction.OnServerInvoke=function(v,m)return q:_handleDelayRequest(m)end q._remoteEvent.OnServerEvent:Connect(function(v)q._remoteEvent:FireClient(v,q:GetTime())end)spawn(function()while true do wait(5)q:Sync()end end)return q end function m.IsSynced(v)return true end function m.GetTime(v)return tick()end function m.Sync(v)local m=v:GetTime()v._remoteEvent:FireAllClients(m)end function m._handleDelayRequest(m,v)local u=m:GetTime()return u-v end local u={}u.__index=u u.ClassName="\083\108\097\118\101\067\108\111\099\107"u._offset=-1 function u.new(v,m)local q=setmetatable({},u)q._remoteEvent=v or error("\078\111 \114\101\109\111\116\101\069\118\101\110\116")q._remoteFunction=m or error("\078\111 \114\101\109\111\116\101\070\117\110\099\116\105\111\110")q._remoteEvent.OnClientEvent:Connect(function(v)q:_handleSyncEvent(v)end)q._remoteEvent:FireServer()return q end function u.GetTime(v)if not v:IsSynced()then warn("\091\083\108\097\118\101\067\108\111\099\107\046\071\101\116\084\105\109\101\093 - \083\108\097\118\101 \099\108\111\099\107 \105\115 \110\111\116 \121\101\116 \115\121\110\099\101\100",debug.traceback())return v:_getLocalTime()end return v:_getLocalTime()-v._offset end function u.IsSynced(v)return v._offset~=-1 end function u._getLocalTime(v)return tick()end function u._handleSyncEvent(m,v)local u=m:_getLocalTime()local q=u-v local d=m:_getLocalTime()local X=m:_sendDelayRequest(d)local T=(q-X)/2 local F=(q+X)/2 m._offset=T m._pneWayDelay=F end function u._sendDelayRequest(m,v)return m._remoteFunction:InvokeServer(v)end local function q()local q=require((game:GetService("\082\101\112\108\105\099\097\116\101\100\083\116\111\114\097\103\101")):WaitForChild("\078\101\118\101\114\109\111\114\101"))local d=q.GetRemoteEvent("\084\105\109\101\083\121\110\099\069\118\101\110\116")local X=q.GetRemoteFunction("\068\101\108\097\121\101\100\082\101\113\117\101\115\116\069\118\101\110\116")if v:IsClient()and v:IsServer()then local v=m.new(d,X)d.OnClientEvent:Connect(function() end)return v elseif v:IsClient()then return u.new(d,X)else return m.new(d,X)end end return q()
--- Syncronize time between client and servers so we can use a universal timestamp
-- across the game.
-- See: www.nist.gov/el/isd/ieee/upload/tutorial-basic.pdf
-- @classmod TimeSyncManager
-- @usage Use use just require the module, it's a singleton. Load TimeSyncManager on the server to use on the clients.

local RunService = game:GetService("RunService")

local MasterClock = {}
MasterClock.__index = MasterClock
MasterClock.ClassName = "MasterClock"

function MasterClock.new(remoteEvent, remoteFunction)
	local self = setmetatable({}, MasterClock)

	self._remoteEvent = remoteEvent or error("No remoteEvent")
	self._remoteFunction = remoteFunction or error("No remoteFunction")

	self._remoteFunction.OnServerInvoke = function(player, timeThree)
		return self:_handleDelayRequest(timeThree)
	end
	self._remoteEvent.OnServerEvent:Connect(function(player)
		 self._remoteEvent:FireClient(player, self:GetTime())
	end)

	spawn(function()
		while true do
			wait(5)
			self:Sync()
		end
	end)

	return self
end

--- Returns true if the manager has synced with the server
-- @treturn boolean
function MasterClock:IsSynced()
	return true
end

--- Returns the sycncronized time
-- @treturn number current time
function MasterClock:GetTime()
	return tick()
end

--- Starts the sync process with all slave clocks.
function MasterClock:Sync()
	local timeOne = self:GetTime()
    self._remoteEvent:FireAllClients(timeOne)
end

--- Client sends back message to get the SM_Difference.
-- @return slaveMasterDifference
function MasterClock:_handleDelayRequest(timeThree)
    local timeFour = self:GetTime()
    return timeFour - timeThree -- -offset + SM Delay
end


local SlaveClock = {}
SlaveClock.__index = SlaveClock
SlaveClock.ClassName = "SlaveClock"
SlaveClock._offset = -1 -- Set uncalculated values to -1

function SlaveClock.new(remoteEvent, remoteFunction)
	local self = setmetatable({}, SlaveClock)

	self._remoteEvent = remoteEvent or error("No remoteEvent")
	self._remoteFunction = remoteFunction or error("No remoteFunction")

	self._remoteEvent.OnClientEvent:Connect(function(timeOne)
		self:_handleSyncEvent(timeOne)
	end)

	self._remoteEvent:FireServer() -- Request server to syncronize with us

	return self
end

function SlaveClock:GetTime()
	if not self:IsSynced() then
		warn("[SlaveClock.GetTime] - Slave clock is not yet synced", debug.traceback())
		return self:_getLocalTime()
	end

	return self:_getLocalTime() - self._offset
end

function SlaveClock:IsSynced()
	return self._offset ~= -1
end

function SlaveClock:_getLocalTime()
    return tick()
end

function SlaveClock:_handleSyncEvent(timeOne)
    local timeTwo = self:_getLocalTime() -- We can't actually get hardware stuff, so we'll send T1 immediately.
    local masterSlaveDifference = timeTwo - timeOne -- We have Offst + MS Delay

    local timeThree = self:_getLocalTime()
    local slaveMasterDifference = self:_sendDelayRequest(timeThree)

    --[[ From explination link.
        The result is that we have the following two equations:
        MS_difference = offset + MS delay
        SM_difference = ?offset + SM delay

        With two measured quantities:
        MS_difference = 90 minutes
        SM_difference = ?20 minutes

        And three unknowns:
        offset , MS delay, and SM delay

        Rearrange the equations according to the tutorial.
        -- Assuming this: MS delay = SM delay = one_way_delay

        one_way_delay = (MSDelay + SMDelay) / 2
    ]]

    local offset = (masterSlaveDifference - slaveMasterDifference)/2
    local oneWayDelay = (masterSlaveDifference + slaveMasterDifference)/2

    self._offset = offset -- Estimated difference between server/client
    self._pneWayDelay = oneWayDelay -- Estimated time for network events to send. (MSDelay/SMDelay)
end

function SlaveClock:_sendDelayRequest(timeThree)
	return self._remoteFunction:InvokeServer(timeThree)
end


--- Return a singleton
local function buildClock()
	local require = require(game:GetService("ReplicatedStorage"):WaitForChild("Nevermore"))

	local remoteEvent = require.GetRemoteEvent("TimeSyncEvent")
	local remoteFunction = require.GetRemoteFunction("DelayedRequestEvent")

	if RunService:IsClient() and RunService:IsServer() then -- Solo test mode
		local clock = MasterClock.new(remoteEvent, remoteFunction)
		remoteEvent.OnClientEvent:Connect(function() end)
		return clock
	elseif RunService:IsClient() then
		return SlaveClock.new(remoteEvent, remoteFunction)
	else
		return MasterClock.new(remoteEvent, remoteFunction)
	end
end

return buildClock()