1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
29 #include "nel/misc/debug.h"
31 #include "time_client.h"
32 #include "interface_v3/interface_manager.h"
33 #include "nel/gui/interface_property.h"
34 #include "client_cfg.h"
35 #include "net_manager.h"
36 #include "game_share/zc_shard_common.h"
38 #include "game_share/light_cycle.h"
53 sint64 T0
= 0; // Time for the last frame.
54 sint64 T1
= 0; // Time for the current frame.
55 sint64 TS
= 0; // Time since the really first Frame.
56 sint64 DT64
= 0; // Diff time with current and last frame in ms.
57 float DT
= 0.f
; // Diff time with current and last frame in sec.
58 TTime TSend
= 0; // Next Time to send motions.
59 TTime DTSend
= 100; // Delta of time to generate the next time to send motions.
60 double TimeInSec
= 0.0; // Time for the current frame in second.
61 double FirstTimeInSec
= 0.0; // Game local origin time
62 float TimeFactor
= 1.f
; // Factor applied to current date (local mode only)
65 TTime LCT
= 1000; // Default LCT (Lag Compensation time).
66 TTime LocalTimeStep
= 100; // Time Step factor on the client.
67 TTime CurrentPacketTime
= 0; // Local Time for the current packet.
68 uint8 CurrentPacket
= 0; // Packet currently valid.
69 uint8 LastPacketReceived
= 0; // Last received packet.
70 uint8 LastPacketAck
= 0; // Last acknowledged packet.
72 NLMISC::TGameCycle LastGameCycle
= 0;
75 CClientDate SmoothedClientDate
;
78 H_AUTO_DECL ( RZ_Client_Update_Time
)
84 // Map with all time for each packet sent to the server.
85 map
<uint8
, TTime
> PacketTime
;
86 // Map with all Infos for each packet received.
87 map
<uint8
, CPacketInfos
> PacketInfos
;
93 //-----------------------------------------------
95 // Remove the packet 'numPacket' from the 'PacketTime' list.
96 //-----------------------------------------------
97 void removePacketTime(uint8 numPacket
)
99 PacketTime
.erase(numPacket
);
100 }// removePacketTime //
102 //-----------------------------------------------
103 // insertPacketTime :
104 // Insert a packet in the PacketTime map.
105 //-----------------------------------------------
106 void insertPacketTime(uint8 numPacket
, TTime time
)
108 PacketTime
.insert(make_pair(numPacket
, time
));
109 }// insertPacketTime //
112 //-----------------------------------------------
113 // checkLocalTimeStep :
114 // Check if the Local Time Step is still valid with the new packet.
115 //-----------------------------------------------
116 void checkLocalTimeStep(const CPacketInfos
&packetInfos
)
118 // Compute how many packets there are since the current packet.
119 sint nbPacket
= packetInfos
.Num
- CurrentPacket
;
123 // \todo GUIGUI : check why this happen.
127 // Compute the predicted Local Time for this packet.
128 TTime predictedTime
= CurrentPacketTime
+ LocalTimeStep
*nbPacket
;
130 TTime localTimeStep
= LocalTimeStep
;
132 // If the predicted time is too small according to the min time computed for the packet -> increase 'LocalTimeStep'.
133 if(predictedTime
< packetInfos
.Min
)
135 // nlwarning("packetInfos.Min : %f", (double)packetInfos.Min);
136 // nlwarning("CurrentPacketTime : %f", (double)CurrentPacketTime);
137 // nlwarning("packetInfos.Min - CurrentPacketTime : %f", (double)((double)packetInfos.Min - (double)CurrentPacketTime));
139 LocalTimeStep
= (packetInfos
.Min
- CurrentPacketTime
)/nbPacket
;
141 // nlwarning("LocalTimeStep : %f", (double)LocalTimeStep);
143 // If the predicted time is too big according to the max time computed for the packet -> increase 'LocalTimeStep'.
144 else if(predictedTime
> packetInfos
.Max
)
146 // nlwarning("packetInfos.Max : %f", (double)packetInfos.Max);
147 // nlwarning("CurrentPacketTime : %f", (double)CurrentPacketTime);
148 // nlwarning("packetInfos.Max - CurrentPacketTime : %f", (double)((double)packetInfos.Max - (double)CurrentPacketTime));
150 LocalTimeStep
= (packetInfos
.Max
- CurrentPacketTime
)/nbPacket
;
152 // nlwarning("LocalTimeStep : %f", (double)LocalTimeStep);
155 if(LocalTimeStep
== 0)
157 LocalTimeStep
= localTimeStep
;
158 // nlwarning("LocalTimeStep (corrected): %f", (double)LocalTimeStep);
160 }// checkLocalTimeStep //
162 //-----------------------------------------------
164 // Remove the packet 'numPacket' from the 'PacketInfos' list.
165 //-----------------------------------------------
166 void removePacket(uint8 numPacket
)
168 PacketInfos
.erase(numPacket
);
171 //-----------------------------------------------
173 // Insert the packet received information in a list.
174 //-----------------------------------------------
175 void insertPacket(const CPacketInfos
&packetInfos
)
177 // Upadte the last packet received.
178 LastPacketReceived
= packetInfos
.Num
;
180 // Insert the packet information.
181 PacketInfos
.insert(make_pair(LastPacketReceived
, packetInfos
));
183 // Check Local Time Step with the new packet.
184 checkLocalTimeStep(packetInfos
);
188 //-----------------------------------------------
189 // getSentPacketTime :
190 // Return the sent time for a given packet or 0 if the packet is not found
191 //-----------------------------------------------
192 TTime
getSentPacketTime(uint8 numPacket
)
194 map
<uint8
, TTime
>::iterator it
= PacketTime
.find(numPacket
);
195 if(it
!= PacketTime
.end())
199 }// getSentPacketTime //
201 //-----------------------------------------------
202 // ackPacketTimeBefore :
204 //-----------------------------------------------
205 void ackPacketTimeBefore(uint8 ackPacketNumber
)
207 // If the acknowledged packet is not in the list -> player do not have send any packet for the time.
208 map
<uint8
, TTime
>::iterator itEnd
= PacketTime
.find(ackPacketNumber
);
209 if(itEnd
!= PacketTime
.end())
211 // If the packet cannot be found -> First valid packet the server is acknowledging
212 map
<uint8
, TTime
>::iterator itBegin
= PacketTime
.find(LastPacketAck
);
213 if(itBegin
!= PacketTime
.end())
215 while(itBegin
!= itEnd
)
218 map
<uint8
, TTime
>::iterator itTmp
= itBegin
;
220 // Destroy packets between the last acknowledged packet and the recent one.
221 PacketTime
.erase(itTmp
);
223 if(itBegin
== PacketTime
.end())
224 itBegin
= PacketTime
.begin();
229 LastPacketAck
= ackPacketNumber
;
230 }// ackPacketTimeBefore //
232 static CInterfaceProperty
*InterfaceTime
= NULL
;
233 static CInterfaceProperty
*InterfaceServerTick
= NULL
;
234 static CInterfaceProperty
*InterfaceSmoothServerTick
= NULL
;
235 static CInterfaceProperty
*InterfaceDay
= NULL
;
236 static CInterfaceProperty
*InterfaceDayBeforeNextZCDistrib
= NULL
;
238 //-----------------------------------------------
241 //-----------------------------------------------
242 void initClientTime()
244 nlassert (InterfaceTime
== NULL
);
245 nlassert (InterfaceServerTick
== NULL
);
246 nlassert (InterfaceSmoothServerTick
== NULL
);
247 nlassert (InterfaceDay
== NULL
);
248 nlassert (InterfaceDayBeforeNextZCDistrib
== NULL
);
250 InterfaceTime
= new CInterfaceProperty
;
251 InterfaceServerTick
= new CInterfaceProperty
;
252 InterfaceSmoothServerTick
= new CInterfaceProperty
;
253 InterfaceDay
= new CInterfaceProperty
;
254 InterfaceDayBeforeNextZCDistrib
= new CInterfaceProperty
;
256 CInterfaceManager
*pIM
= CInterfaceManager::getInstance();
258 // get a direct link to the database
259 NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:CURRENT_TIME");
260 InterfaceTime
->link("UI:VARIABLES:CURRENT_TIME");
261 NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:CURRENT_SERVER_TICK");
262 InterfaceServerTick
->link("UI:VARIABLES:CURRENT_SERVER_TICK");
263 NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:CURRENT_SMOOTH_SERVER_TICK");
264 InterfaceSmoothServerTick
->link("UI:VARIABLES:CURRENT_SMOOTH_SERVER_TICK");
265 NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:CURRENT_DAY");
266 InterfaceDay
->link("UI:VARIABLES:CURRENT_DAY");
267 NLGUI::CDBManager::getInstance()->getDbProp("UI:VARIABLES:DAY_BEFORE_ZC_DISTRIB");
268 InterfaceDayBeforeNextZCDistrib
->link("UI:VARIABLES:DAY_BEFORE_ZC_DISTRIB");
271 //-----------------------------------------------
273 void releaseClientTime()
277 delete InterfaceTime
;
278 InterfaceTime
= NULL
;
280 if (InterfaceServerTick
)
282 delete InterfaceServerTick
;
283 InterfaceServerTick
= NULL
;
285 if (InterfaceSmoothServerTick
)
287 delete InterfaceSmoothServerTick
;
288 InterfaceSmoothServerTick
= NULL
;
295 if (InterfaceDayBeforeNextZCDistrib
)
297 delete InterfaceDayBeforeNextZCDistrib
;
298 InterfaceDayBeforeNextZCDistrib
= NULL
;
305 //-----------------------------------------------
306 // updateClientTime :
308 //-----------------------------------------------
309 CWidgetManager::SInterfaceTimes times
;
311 void updateClientTime()
313 H_AUTO_USE ( RZ_Client_Update_Time
)
318 // Constant delta time ?
319 if (ClientCfg
.ForceDeltaTime
== 0)
321 T1
= ryzomGetLocalTime();
324 // This case is known to occurs if the framerate is very fast (e.g. during the
325 // loading stage) or if the machine has got a heavy load (e.g. lots of AGP data)
326 // delaying the timer updates.
327 //nlwarning ("getLocalTime has returned the same time! START");
330 T1
= ryzomGetLocalTime();
331 // give up the time slice to let time to other process
334 //nlwarning ("getLocalTime has returned the same time! END");
340 static bool firstTime
= true;
343 T1
= ryzomGetLocalTime();
348 T1
+= ClientCfg
.ForceDeltaTime
;
353 nlwarning("updateClientTime: T0 should not be = T1.");
355 // Update time variables
358 DT
= ((float)DT64
)*0.001f
;
360 TimeInSec
= T1
*0.001;
363 if (FirstTimeInSec
== 0.0)
364 FirstTimeInSec
= TimeInSec
;
366 // Set the InterfaceManager current time.
368 InterfaceTime
->setSInt64(T1
);
369 if (InterfaceServerTick
)
370 InterfaceServerTick
->setSInt64(NetMngr
.getCurrentServerTick());
371 // Set the Smooth ServerTick
372 if (InterfaceSmoothServerTick
)
373 InterfaceSmoothServerTick
->setSInt64(NetMngr
.getCurrentSmoothServerTick());
375 // Day, and Day before ZC distrib
377 InterfaceDay
->setSInt64(RT
.getRyzomDay());
378 if(InterfaceDayBeforeNextZCDistrib
)
380 sint d
= RT
.getRyzomDay()%ZCSTATE::ZCDistribPeriod
;
381 // Must dispaly at least "1".
382 d
= ZCSTATE::ZCDistribPeriod
- d
;
383 InterfaceDayBeforeNextZCDistrib
->setSInt64(d
);
386 #ifdef ENABLE_INCOMING_MSG_RECORDER
387 // Init For the Replay.
388 if(NetMngr
.isReplayStarting())
389 NetMngr
.startReplay();
392 times
.lastFrameMs
= T0
;
393 times
.thisFrameMs
= T1
;
394 times
.frameDiffMs
= DT64
;
396 CWidgetManager::getInstance()->updateInterfaceTimes( times
);
398 }// updateClientTime //
401 // update smoothed time
402 void updateSmoothedTime()
404 #define REAL_SECONDS_TO_RYZOM_HOUR(s) (double(s) * 10 / double(RYZOM_HOURS_IN_TICKS))
406 CClientDate oldSmoothedClientDate
= SmoothedClientDate
;
407 double dHour
= REAL_SECONDS_TO_RYZOM_HOUR(DT
);
408 SmoothedClientDate
.Hour
+= (float) dHour
;
409 if (SmoothedClientDate
.Hour
> WorldLightCycle
.NumHours
)
411 SmoothedClientDate
.Day
+= (sint32
) ceil(dHour
/ WorldLightCycle
.NumHours
);
412 SmoothedClientDate
.Hour
= (float) fmod(SmoothedClientDate
.Hour
, WorldLightCycle
.NumHours
);
414 double smoothedClientDateInHour
= (double) SmoothedClientDate
.Day
* 24 + SmoothedClientDate
.Hour
;
415 double ryzomDateInHour
= (double) RT
.getRyzomDay() * 24 + (double) RT
.getRyzomTime();
416 const double SMOOTHED_DATE_MAX_ERROR_IN_RYZOM_HOUR
= REAL_SECONDS_TO_RYZOM_HOUR(10);
417 double hourError
= fabs(smoothedClientDateInHour
- ryzomDateInHour
);
418 if (hourError
> SMOOTHED_DATE_MAX_ERROR_IN_RYZOM_HOUR
)
420 if (smoothedClientDateInHour
< ryzomDateInHour
)
422 // smoothed time is late
423 SmoothedClientDate
.Day
= RT
.getRyzomDay();
424 SmoothedClientDate
.Hour
= RT
.getRyzomTime();
428 if (hourError
> (2 * SMOOTHED_DATE_MAX_ERROR_IN_RYZOM_HOUR
))
430 // if difference is too big, rescale smoothed time on server time
431 SmoothedClientDate
.Day
= RT
.getRyzomDay();
432 SmoothedClientDate
.Hour
= RT
.getRyzomTime();
436 // cancel time increment (wait until server time is up with smoothed client time)
437 SmoothedClientDate
= oldSmoothedClientDate
;