1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2016 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
22 #include "nel/net/callback_client.h"
23 #include "nel/net/callback_server.h"
24 #include "nel/net/naming_client.h"
25 #include "nel/net/message.h"
27 #include "nel/net/unitime.h"
29 using namespace NLMISC
;
35 TTime
_CUniTime::_SyncUniTime
= 0;
36 TTime
_CUniTime::_SyncLocalTime
= 0;
37 bool _CUniTime::_Simulate
= false;
39 bool _CUniTime::Sync
= false;
42 void _CUniTime::setUniTime (NLMISC::TTime
/* uTime */, NLMISC::TTime
/* lTime */)
47 TTime lt = getLocalTime ();
48 TTime delta = uTime - lTime + _SyncLocalTime - _SyncUniTime;
50 nlinfo ("_CUniTime::setUniTime(%" NL_I64 "d, %" NL_I64 "d): Resyncing delta %" NL_I64 "dms",uTime,lTime,delta);
54 nlinfo ("_CUniTime::setUniTime(%" NL_I64 "d, %" NL_I64 "d)",uTime,lTime);
58 _SyncLocalTime = lTime;
61 void _CUniTime::setUniTime (NLMISC::TTime
/* uTime */)
64 // setUniTime (uTime, getLocalTime ());
69 TTime
_CUniTime::getUniTime ()
75 nlerror ("called getUniTime before calling syncUniTimeFromServer");
77 return getLocalTime () - (_SyncLocalTime - _SyncUniTime);
82 const char *_CUniTime::getStringUniTime ()
85 return getStringUniTime(_CUniTime::getUniTime());
89 const char *_CUniTime::getStringUniTime (TTime ut
)
94 uint32 ms
= (uint32
) (ut
% 1000); // time in ms 1000ms dans 1s
97 uint32 s
= (uint32
) (ut
% 60); // time in seconds 60s dans 1mn
100 uint32 m
= (uint32
) (ut
% 60); // time in minutes 60m dans 1h
103 uint32 h
= (uint32
) (ut
% 9); // time in hours 9h dans 1j
106 uint32 day
= (uint32
) (ut
% (8*4)); // time in days 8day dans 1month
109 uint32 week
= (uint32
) (ut
% 4); // time in weeks 4week dans 1month
112 uint32 month
= (uint32
) (ut
% 12); // time in months 12month dans 1year
115 uint year
= (uint32
) ut
; // time in years
117 smprintf (str
, 512, "%02d/%02d/%04d (week %d) %02d:%02d:%02d.%03d", day
+1, month
+1, year
+1, week
+1, h
, m
, s
, ms
);
121 ////////////////////////////////////////////////////////////////////////////////////////////////////
122 ////////////////////////////////////////////////////////////////////////////////////////////////////
123 /////////////// SYNCHRONISATION BETWEEN TIME SERVICE AND OTHER SERVICES ////////////////////////////
124 ////////////////////////////////////////////////////////////////////////////////////////////////////
125 ////////////////////////////////////////////////////////////////////////////////////////////////////
127 static bool GetUniversalTime
;
128 static uint32 GetUniversalTimeSecondsSince1970
;
129 static TTime GetUniversalTimeUniTime
;
132 static void cbGetUniversalTime (CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
135 // get the association between a date and unitime
136 msgin.serial (GetUniversalTimeSecondsSince1970);
137 msgin.serial (GetUniversalTimeUniTime);
138 GetUniversalTime = true;
142 /***************************************************************/
143 /******************* THE FOLLOWING CODE IS COMMENTED OUT *******/
144 /***************************************************************
145 static TCallbackItem UniTimeCallbackArray[] =
147 { "GUT", cbGetUniversalTime }
149 ***************************************************************/
151 void _CUniTime::syncUniTimeFromService (CCallbackNetBase::TRecordingState
/* rec */, const CInetAddress
* /* addr */)
154 /***************************************************************/
155 /******************* THE FOLLOWING CODE IS COMMENTED OUT *******/
156 /***************************************************************
157 TTime deltaAdjust, lt;
158 uint32 firstsecond, nextsecond;
159 TTime before, after, delta;
161 // create a message with type in the full text format
162 CMessage msgout ("AUT");
163 CCallbackClient server( rec, "TS.nmr" );
164 server.addCallbackArray (UniTimeCallbackArray, sizeof (UniTimeCallbackArray) / sizeof (UniTimeCallbackArray[0]));
168 CNamingClient::lookupAndConnect ("TS", server);
172 server.connect (*addr);
175 if (!server.connected()) goto error;
177 server.send (msgout);
180 before = CTime::getLocalTime ();
182 // receive the answer
183 GetUniversalTime = false;
184 while (!GetUniversalTime)
186 if (!server.connected()) goto error;
193 // after, before and delta is not used. It's only for information purpose.
194 after = CTime::getLocalTime ();
195 delta = after - before;
197 nlinfo ("_CUniTime::syncUniTimeFromService(): ping:%" NL_I64 "dms, time:%ds, unitime:%" NL_I64 "dms", delta, GetUniversalTimeSecondsSince1970, GetUniversalTimeUniTime);
199 // <-- from here to the "-->" comment, the block must be executed in less than one second or an infinite loop occurs
202 firstsecond = CTime::getSecondsSince1970 ();
203 nextsecond = firstsecond+1;
205 // wait the next start of the second (take 100% of CPU to be more accurate)
206 while (nextsecond != CTime::getSecondsSince1970 ())
207 nlassert (CTime::getSecondsSince1970 () <= nextsecond);
211 // get the local time of the beginning of the next second
212 lt = CTime::getLocalTime ();
216 if (abs((sint32)((TTime)nextsecond - (TTime)GetUniversalTimeSecondsSince1970)) > 10)
218 nlerror ("the time delta (between me and the Time Service) is too big (more than 10s), servers aren't NTP synchronized");
222 // compute the delta between the other side and our side number of second since 1970
223 deltaAdjust = ((TTime) nextsecond - (TTime) GetUniversalTimeSecondsSince1970) * 1000;
225 // adjust the unitime to the current localtime
226 GetUniversalTimeUniTime += deltaAdjust;
228 nlinfo ("_CUniTime::syncUniTimeFromService(): rtime:%ds, runitime:%" NL_I64 "ds, rlocaltime:%" NL_I64 "d, deltaAjust:%" NL_I64 "dms", nextsecond, GetUniversalTimeUniTime, lt, deltaAdjust);
232 nlinfo ("_CUniTime::syncUniTimeFromService(): runitime:%" NL_I64 "ds, rlocaltime:%" NL_I64 "d", GetUniversalTimeUniTime, lt);
235 _CUniTime::setUniTime (GetUniversalTimeUniTime, lt);
237 server.disconnect ();
241 nlerror ("Time Service is not found, lost or can't synchronize universal time");
242 ***************************************************************/
248 ////////////////////////////////////////////////////////////////////////////////////////////////////
249 ////////////////////////////////////////////////////////////////////////////////////////////////////
250 /////////////// SYNCHRONISATION BETWEEN CLIENT AND SHARD ///////////////////////////////////////////
251 ////////////////////////////////////////////////////////////////////////////////////////////////////
252 ////////////////////////////////////////////////////////////////////////////////////////////////////
256 static void cbServerAskUniversalTime (CMessage
& /* msgin */, TSockId from
, CCallbackNetBase
&netbase
)
259 TTime ut
= _CUniTime::getUniTime ();
261 // afficher l adresse de celui qui demande
262 nlinfo("UT: Send the universal time %" NL_I64
"d to '%s'", ut
, netbase
.hostAddress(from
).asString().c_str());
264 CMessage
msgout ("GUT");
266 netbase
.send (msgout
, from
);
269 TCallbackItem ServerTimeServiceCallbackArray
[] =
271 { "AUT", cbServerAskUniversalTime
},
274 void _CUniTime::installServer (CCallbackServer
*server
)
277 static bool alreadyAddedCallback
= false;
278 nlassert (server
!= NULL
);
279 nlassert (!alreadyAddedCallback
);
281 server
->addCallbackArray (ServerTimeServiceCallbackArray
, sizeof (ServerTimeServiceCallbackArray
) / sizeof (ServerTimeServiceCallbackArray
[0]));
282 alreadyAddedCallback
= true;
287 static bool GetClientUniversalTime
;
288 static TTime GetClientUniversalTimeUniTime
;
291 static void cbClientGetUniversalTime (CMessage &msgin, TSockId from, CCallbackNetBase &netbase)
294 // get the association between a date and unitime
295 msgin.serial (GetClientUniversalTimeUniTime);
296 GetClientUniversalTime = true;
300 /***************************************************************/
301 /******************* THE FOLLOWING CODE IS COMMENTED OUT *******/
302 /***************************************************************
303 static TCallbackItem ClientUniTimeCallbackArray[] =
305 { "GUT", cbClientGetUniversalTime }
307 ***************************************************************/
310 void _CUniTime::syncUniTimeFromServer (CCallbackClient
* /* client */)
313 /***************************************************************/
314 /******************* THE FOLLOWING CODE IS COMMENTED OUT *******/
315 /***************************************************************
317 static bool alreadyAddedCallback = false;
318 nlassert (client != NULL);
320 if (!alreadyAddedCallback)
322 client->addCallbackArray (ClientUniTimeCallbackArray, sizeof (ClientUniTimeCallbackArray) / sizeof (ClientUniTimeCallbackArray[0]));
323 alreadyAddedCallback = true;
327 TTime bestdelta = 60000; // 1 minute
329 if (!client->connected ()) goto error;
333 CMessage msgout ("AUT");
335 if (!client->connected()) goto error;
338 client->send (msgout);
341 TTime before = CTime::getLocalTime ();
343 // receive the answer
344 GetClientUniversalTime = false;
345 while (!GetClientUniversalTime)
347 if (!client->connected()) goto error;
352 TTime after = CTime::getLocalTime (), delta = after - before;
354 if (delta < 10 || delta < bestdelta)
358 _CUniTime::setUniTime (GetClientUniversalTimeUniTime, (before+after)/2);
360 if (delta < 10) break;
364 client->disconnect ();
365 nlinfo ("Universal time is %" NL_I64 "dms with a mean error of %" NL_I64 "dms", _CUniTime::getUniTime(), bestdelta/2);
368 nlwarning ("there's no connection or lost or can't synchronize universal time");
369 ***************************************************************/
377 NLMISC_CATEGORISED_COMMAND(nel, time, "displays the universal time", "")
379 if(args.size() != 0) return false;
381 if ( _CUniTime::Sync )
383 log.displayNL ("CTime::getLocalTime(): %" NL_I64 "dms, _CUniTime::getUniTime(): %" NL_I64 "dms", CTime::getLocalTime (), _CUniTime::getUniTime ());
384 log.displayNL ("_CUniTime::getStringUniTime(): '%s'", _CUniTime::getStringUniTime());
388 log.displayNL ("CTime::getLocalTime(): %" NL_I64 "dms <Universal time not sync>", CTime::getLocalTime ());