/
TimeSyncManager.lua
154 lines (117 loc) · 4.42 KB
/
TimeSyncManager.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
--- 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")
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()