Use configured resolution for login/outgame/ingame
[ryzomcore.git] / ryzom / client / src / network_connection.cpp
blobbad168a60a81eb5407cc194253c0714f547b850f
1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 //
8 // This program is free software: you can redistribute it and/or modify
9 // it under the terms of the GNU Affero General Public License as
10 // published by the Free Software Foundation, either version 3 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU Affero General Public License for more details.
18 // You should have received a copy of the GNU Affero General Public License
19 // along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include "stdpch.h"
25 #ifdef NL_OS_WINDOWS
26 #include <process.h>
27 #endif
29 #include "network_connection.h"
30 #include "client_cfg.h"
32 #include "game_share/action_factory.h"
33 #include "game_share/action.h"
34 #include "game_share/action_disconnection.h"
35 #include "game_share/action_position.h"
36 #include "game_share/action_sync.h"
37 #include "game_share/action_association.h"
38 #include "game_share/action_dummy.h"
39 #include "game_share/action_login.h"
40 #include "game_share/action_sint64.h"
41 #include "game_share/action_target_slot.h"
42 #include "game_share/action_generic.h"
43 #include "game_share/action_generic_multi_part.h"
44 #include "game_share/mode_and_behaviour.h"
46 #include "game_share/simlag.h"
48 #include "nel/misc/cdb.h"
49 #include "nel/misc/cdb_leaf.h"
50 #include "nel/misc/cdb_branch.h"
51 #include "cdb_synchronised.h"
53 #include "nel/misc/variable.h"
54 #include "nel/misc/algo.h"
55 #include "nel/3d/u_driver.h"
57 #include "game_share/system_message.h"
59 #include "game_share/entity_types.h" // required for ifdef
61 #include "graph.h"
63 #include "global.h"
64 #include "far_tp.h"
66 #ifdef DISPLAY_ENTITIES
67 #include "../../../test/network/sb5000/client/graph.h"
68 #endif
71 // ***************************************************************************
72 // Smooth ServerTick setup
73 // The numner of Smooth Tick per Server Tick
74 #define SMOOTH_SERVER_TICK_PER_TICK 100
75 // The possible not corrected error between actual ServerTick and estimated one (NB: equal to packet sent frequency)
76 #define SMOOTH_SERVER_TICK_WINDOW 100
77 // The min Difference between the estimated and the actual one. If <=, the estimation is reseted.
78 #define SMOOTH_SERVER_TICK_DIFF_MIN -1000
79 // The max Difference between the estimated and the actual one. If >=, clamp
80 #define SMOOTH_SERVER_TICK_DIFF_MAX 500
81 // When the estimation is late, the Max Acceleration factor
82 #define SMOOTH_SERVER_TICK_ACCEL 4.0f
85 const char * ConnectionStateCStr [9] = { "NotInitialised", "NotConnected", "Authenticate", "Login", "Synchronize", "Connected", "Probe", "Stalled", "Disconnect" };
88 // ***************************************************************************
89 using namespace std;
90 using namespace NLMISC;
91 using namespace NLNET;
92 using namespace CLFECOMMON;
95 #undef MEASURE_RECEIVE_DATES
96 //#define SHOW_PROPERTIES_RECEIVED
98 #ifdef MEASURE_RECEIVE_DATES
99 #include <nel/misc/config_file.h>
100 #include <nel/misc/displayer.h>
101 #include <nel/misc/log.h>
102 #include <nel/misc/path.h>
103 #include <nel/misc/time_nl.h>
104 #include <nel/misc/command.h>
106 #ifdef DEBUG_NEW
107 #define new DEBUG_NEW
108 #endif
110 // Stat: array of vectors of cycles when a pos is received, indexed by TCLEntityId
111 struct TRDateState
113 TRDateState( TGameCycle gc, TGameCycle pdit, TTime ct ) : ServerCycle(gc), PredictedInterval(pdit), LocalTime(ct) {}
115 TGameCycle ServerCycle, PredictedInterval;
116 TTicks LocalTime;
120 extern CLFECOMMON::TCLEntityId WatchedEntitySlot;
122 ////////////
123 // GLOBAL //
124 ////////////
127 typedef vector<TRDateState> TReceiveDateLog;
128 TReceiveDateLog ReceivePosDateLog [256];
129 bool LogReceiveEnabled = false;
130 CConfigFile NCConfigFile;
131 CFileDisplayer *ReceiveLogDisp;
132 CLog ReceiveLogger;
136 * initReceiveLog
138 void initReceiveLog()
142 ReceiveLogDisp = new CFileDisplayer( getLogDirectory() + "ReceiveLog.log" );
143 ReceiveLogger.addDisplayer( ReceiveLogDisp );
144 //ReceiveLogger.displayNL( "Searching for param LogReceive in the config file..." );
145 NCConfigFile.load( string( "client.cfg" ) );
146 int slot = NCConfigFile.getVar( "LogReceive" ).asInt();
147 if ( slot != 0 )
149 LogReceiveEnabled = true;
150 ReceiveLogger.displayNL( "LogReceive is on" ); // only when enabled
153 catch (const EConfigFile&)
158 * startReceiveLog (the slots logged are all if no selection, or only the selected slots (one at a time)
160 void startReceiveLog()
162 sint i;
163 for ( i=0; i!=256; ++i )
165 ReceivePosDateLog[i].clear();
168 LogReceiveEnabled = true;
172 * stopReceiveLog
174 void stopReceiveLog()
176 LogReceiveEnabled = false;
180 * displayReceiveLog
182 void displayReceiveLog()
184 if ( ! LogReceiveEnabled )
185 return;
187 nlinfo( "Dumping ReceiveLog" );
188 ReceiveLogger.displayNL( "ReceiveLog (ServerCycle, PredictedInterval, LocalTime(ms)):" );
190 // Display receive dates for all slots for which vector is not empty
191 // (allows to trace several selected slots one after one, or all slots at the same time)
192 sint i;
193 for ( i=0; i!=256; ++i )
195 if ( ! ReceivePosDateLog[i].empty() )
197 ReceiveLogger.displayRawNL( "Entity %d: %u updates", i, ReceivePosDateLog[i].size() );
198 TReceiveDateLog::iterator idl;
199 for ( idl=ReceivePosDateLog[i].begin(); idl!=ReceivePosDateLog[i].end(); ++idl )
201 ReceiveLogger.displayRawNL( "%u\t%u\t%" NL_I64 "u", (*idl).ServerCycle, (*idl).PredictedInterval, (*idl).LocalTime );
205 ReceiveLogger.displayRawNL( "ReceiveLog completed" );
208 NLMISC_COMMAND( startReceiveLog, "Starts logging the position receives (for all or only the watched entities when selected)", "" )
210 nlinfo( "Starting ReceiveLog" );
211 startReceiveLog();
212 return true;
215 NLMISC_COMMAND( stopReceiveLog, "Stops logging the position receives", "" )
217 stopReceiveLog();
218 return true;
221 NLMISC_COMMAND( displayReceiveLog, "Flush the receive log into ReceiveLog.log", "" )
223 displayReceiveLog();
224 return true;
227 #else
229 #ifdef DEBUG_NEW
230 #define new DEBUG_NEW
231 #endif
233 #endif // MEASURE_RECEIVE_DATES
235 extern NL3D::UDriver *Driver;
238 CVariable<bool> CheckXMLSignature("client", "CheckXMLSignature", "enable client to check msg/database.xml signature", true, 0, true);
240 CSlotGraph *PosUpdateIntervalGraph = NULL;
241 CSlotGraph *PosUpdatePredictionGraph = NULL;
243 CGraph MsPerTickGraph ("mspertick (ms)", 10.0f, 570.0f, 400.0f, 100.0f, CRGBA(0,0,128,128), 1000, 400.0f, 2);
244 CGraph PingGraph ("ping (ms)", 10.0f, 460.0f, 400.0f, 100.0f, CRGBA(128,0,0,128), 100000, 1000.0f, 2);
245 CGraph PacketLossGraph ("PacketLoss (pc)", 10.0f, 350.0f, 200.0f, 100.0f, CRGBA(0,128,0,128), 100000, 100.0f, 2);
246 CGraph UploadGraph ("upload (byps)", 10.0f, 240.0f, 200.0f, 100.0f, CRGBA(0,128,128,128), 1000, 3000.0f, 2);
247 CGraph DownloadGraph ("download (byps)", 10.0f, 130.0f, 200.0f, 100.0f, CRGBA(0,0,128,128), 1000, 3000.0f, 2);
250 //#define A a() !!!
251 //#define B b() !!! SO DANGEROUS !!!
252 //#define Parent parent() !!!
255 bool CNetworkConnection::_Registered = false;
259 * Percentile (in fact, quantile) function
260 * Freely adapted from the Goose Library (http://www.gnu.org/software/goose)
261 * at http://cvs.gnome.org/lxr/source/goose/src/stats/descriptive.cpp#90
264 // Normal quantile function (see also percentilRev below)
265 /*float percentile( const multiset<uint32>& dataset, float p )
267 //nlassert( ! dataset.empty() );
268 //nlassert( (p >= 0) && (p <= 1) );
269 uint ds = dataset.size();
270 if ( ds == 1 )
271 return (float)(*dataset.begin());
272 float fpIndex = p * (float)(ds-1);
273 sint ipIndex = (sint)fpIndex;
274 multiset<uint32>::iterator it = dataset.begin(), itnext;
275 for ( sint i=0; i!=ipIndex; ++i, ++it );
276 itnext = it; ++itnext;
277 return ((float)(ipIndex+1)-fpIndex)*(float)(*it) + (fpIndex-(float)ipIndex)*(float)(*itnext);
280 // Reversed quantile function = percentile(dataset, 1-p) (optimized for p>0.5 i.e. rp<=0.5)
281 float percentileRev( const multiset<uint32>& dataset, float rp )
283 //nlassert( ! dataset.empty() );
284 //nlassert( (rp >= 0) && (rp <= 1) );
285 uint ds = (uint)dataset.size();
286 if ( ds == 1 )
287 return (float)(*dataset.begin());
288 float fpIndex = rp * (float)(ds-1);
289 sint ipIndex = (sint)fpIndex;
290 multiset<uint32>::const_reverse_iterator it = dataset.rbegin(), itnext;
291 for ( sint i=0; i!=ipIndex; ++i, ++it );
292 itnext = it; ++itnext;
293 return ((float)(ipIndex+1)-fpIndex)*(float)(*it) + (fpIndex-(float)ipIndex)*(float)(*itnext);
299 const uint MAX_POSUPDATETICKQUEUE_SIZE = 25;
300 const float PREDICTION_REV_PERCENTILE = 0.2f; // (1 - 0.8)
304 bool CNetworkConnection::LoggingMode = false;
308 * Constructor
310 #ifdef ENABLE_INCOMING_MSG_RECORDER
311 CNetworkConnection::CNetworkConnection() : _NextMessageToReplay(true)
312 #else
313 CNetworkConnection::CNetworkConnection()
314 #endif
316 _ConnectionState = NotInitialised;
317 _ImpulseCallback = NULL;
318 _ImpulseArg = NULL;
319 _DataBase = NULL;
321 reset();
323 _QuitId = 0;
325 initTicks();
330 * Destructor
332 CNetworkConnection::~CNetworkConnection()
334 #ifdef MEASURE_RECEIVE_DATES
335 delete ReceiveLogDisp;
336 #endif
337 if ( _VisualPropertyTreeRoot ) // not allocated in local mode
339 _VisualPropertyTreeRoot->deleteBranches();
340 delete _VisualPropertyTreeRoot;
341 _VisualPropertyTreeRoot = NULL;
344 #ifdef ENABLE_INCOMING_MSG_RECORDER
345 if ( _RecordIncomingMessagesOn )
346 _RecordedMessagesOut.close();
347 else if ( _ReplayIncomingMessagesOn )
348 _RecordedMessagesIn.close();
349 #endif
352 NLMISC::CHashKeyMD5 getTextMD5(const std::string& filename)
354 CIFile fi;
355 if (!fi.open(CPath::lookup(filename, false)))
356 return NLMISC::CHashKeyMD5();
358 std::vector<uint8> buffer(fi.getFileSize());
359 fi.serialBuffer(&(buffer[0]), (uint)buffer.size());
361 std::vector<uint8>::iterator it = buffer.begin();
364 while (it != buffer.end() && *it != '\015')
365 ++it;
367 if (it != buffer.end())
368 it = buffer.erase(it);
370 while (it != buffer.end());
372 return NLMISC::getMD5((&buffer[0]), (uint32)buffer.size());
376 * Init
378 void CNetworkConnection::init(const string &cookie, const string &addr)
380 if (_ConnectionState != NotInitialised &&
381 _ConnectionState != Disconnect)
383 nlwarning("Unable to init(): connection not properly closed yet.");
384 return;
387 if (!_Registered)
389 CActionFactory::getInstance ()->registerAction (ACTION_POSITION_CODE, CActionPosition::create);
390 CActionFactory::getInstance ()->registerAction (ACTION_SYNC_CODE, CActionSync::create);
391 CActionFactory::getInstance ()->registerAction (ACTION_DISCONNECTION_CODE, CActionDisconnection::create);
392 CActionFactory::getInstance ()->registerAction (ACTION_ASSOCIATION_CODE, CActionAssociation::create);
393 CActionFactory::getInstance ()->registerAction (ACTION_DUMMY_CODE, CActionDummy::create);
394 CActionFactory::getInstance ()->registerAction (ACTION_LOGIN_CODE, CActionLogin::create);
395 CActionFactory::getInstance ()->registerAction (ACTION_TARGET_SLOT_CODE, CActionTargetSlot::create);
396 CActionFactory::getInstance ()->registerAction (ACTION_GENERIC_CODE, CActionGeneric::create);
397 CActionFactory::getInstance ()->registerAction (ACTION_GENERIC_MULTI_PART_CODE, CActionGenericMultiPart::create);
398 CActionFactory::getInstance ()->registerAction (ACTION_SINT64, CActionSint64::create);
399 _Registered = true;
402 initCookie(cookie, addr);
404 #ifdef HALF_FREQUENCY_SENDING_TO_CLIENT
405 nlinfo( "Half-frequency mode" );
406 #else
407 nlinfo( "Full-frequency mode" );
408 #endif
410 #ifdef MEASURE_RECEIVE_DATES
411 initReceiveLog();
412 #endif
414 // Register property nbbits
415 CActionSint64::registerNumericPropertiesRyzom();
417 // Init visual property tree
418 _VisualPropertyTreeRoot = new TVPNodeClient();
419 _VisualPropertyTreeRoot->buildTree();
421 #ifdef ENABLE_INCOMING_MSG_RECORDER
422 _RecordIncomingMessagesOn = false;
423 _ReplayIncomingMessagesOn = false;
424 _NextClientTickToReplay = 0;
425 #endif
427 // If the server run on window, those are the one to test
428 _AltMsgXmlMD5 = NLMISC::getMD5("msg.xml");
429 _AltDatabaseXmlMD5 = NLMISC::getMD5("database.xml");
430 // If the server run on UNIX, those are the one to test
431 _MsgXmlMD5 = getTextMD5("msg.xml");
432 _DatabaseXmlMD5 = getTextMD5("database.xml");
437 * Sets the cookie and front-end address, resets the connection state.
439 void CNetworkConnection::initCookie(const string &cookie, const string &addr)
441 #ifdef ENABLE_INCOMING_MSG_RECORDER
442 if ( ! ClientCfg.Local )
443 #endif
445 // set values of simulation for the udp socket
446 CUdpSimSock::setSimValues(ClientCfg.ConfigFile);
448 _FrontendAddress = addr;
450 if (!cookie.empty ())
451 _LoginCookie.setFromString (cookie);
452 else if (ClientCfg.ConfigFile.exists ("UserId"))
454 uint32 uid = ClientCfg.ConfigFile.getVar ("UserId").asInt();
455 _LoginCookie.set(0, 0, uid);
456 Cookie = _LoginCookie.toString(); // to be able to do '/reconnect'
457 nlinfo ("Set the cookie with the UserId %d set in config file", uid);
460 nlinfo ("Network initialisation with front end '%s' and cookie %s",_FrontendAddress.c_str(), _LoginCookie.toString().c_str ());
463 _ConnectionState = NotConnected;
469 namespace CLFECOMMON
471 // Factory for TVPNodeBase::buildTree()
472 TVPNodeBase *NewNode()
474 return (TVPNodeBase*) new CNetworkConnection::TVPNodeClient();
479 #ifdef ENABLE_INCOMING_MSG_RECORDER
481 * Start/stop recording the incoming messages (in online mode)
483 void CNetworkConnection::setRecordingMode( bool onOff, const std::string& filename )
485 nlassert( ! ClientCfg.Local );
486 if ( onOff )
488 if ( ! _RecordIncomingMessagesOn )
490 if ( _RecordedMessagesOut.open( filename ) )
492 nlinfo( "Beginning recording to %s", filename.c_str() );
493 _RecordedMessagesOut.serialEnum( _ConnectionState );
494 _RecordedMessagesOut.serial( _CurrentReceivedNumber );
495 _RecordedMessagesOut.serial( _LastReceivedNumber );
496 _RecordedMessagesOut.serial( _LastAckInLongAck );
497 _RecordIncomingMessagesOn = true;
499 else
501 nlwarning( "Cannot open %s for recording", filename.c_str() );
505 else
507 if ( _RecordIncomingMessagesOn )
508 _RecordedMessagesOut.close();
509 _RecordIncomingMessagesOn = false;
515 * Start/stop replaying the incoming messages (in offline mode)
517 void CNetworkConnection::setReplayingMode( bool onOff, const std::string& filename )
519 nlassert( ClientCfg.Local );
520 if ( onOff )
522 if ( ! _ReplayIncomingMessagesOn )
524 if ( _RecordedMessagesIn.open( filename ) )
526 nlinfo( "Beginning replaying of %s", filename.c_str() );
527 _RecordedMessagesIn.serialEnum( _ConnectionState );
528 _RecordedMessagesIn.serial( _CurrentReceivedNumber );
529 _RecordedMessagesIn.serial( _LastReceivedNumber );
530 _RecordedMessagesIn.serial( _LastAckInLongAck );
531 // Preload first message
532 if ( _RecordedMessagesIn.eof() )
534 // Nothing to load
535 nlinfo( "Nothing to replay" );
536 _RecordedMessagesIn.close();
537 return;
539 else
541 _RecordedMessagesIn.serial( _NextClientTickToReplay );
542 _RecordedMessagesIn.serialMemStream( _NextMessageToReplay );
543 _CurrentClientTick = _NextClientTickToReplay;
544 _CurrentServerTick = _CurrentClientTick + 10;
545 nlinfo( "Setting initial replay tick: %u", _CurrentClientTick );
546 _ReplayIncomingMessagesOn = true;
549 else
551 nlwarning( "File %s for replay not found", filename.c_str() );
555 else
557 if ( _RecordIncomingMessagesOn )
558 _RecordedMessagesIn.close();
559 _ReplayIncomingMessagesOn = false;
562 #endif
565 bool CNetworkConnection::connect(string &result)
567 if (_ConnectionState != NotConnected)
569 nlwarning("Unable to connect(): connection not properly initialised (maybe connection not closed).");
570 return false;
573 // try to find where common data are located depending to where we'll connect
574 // the goal is to use the same database.txt as the server one
577 CConfigFile cfg;
578 cfg.load (CPath::lookup("shards.cfg"));
580 // do this process only if Use == 1
581 if (cfg.getVar("Use").asInt() == 1)
583 CInetAddress fsaddr (_FrontendAddress);
585 fsaddr.setPort(0);
587 nlinfo ("Try to find a shard that have fsaddr = '%s'", fsaddr.asString().c_str());
589 CConfigFile::CVar &shards = cfg.getVar("Shards");
590 CInetAddress net;
591 uint i;
592 for (i = 0; i < shards.size(); i+=2)
596 net.setNameAndPort (shards.asString(i));
597 nlinfo ("testAddr = '%s'", net.asString().c_str());
598 if (net == fsaddr)
600 // ok, we found it, now overwrite files
602 string srcPath = CPath::standardizeDosPath(CPath::getFullPath(shards.asString(i+1)));
604 nlinfo ("srcPath = '%s'", srcPath.c_str());
606 CConfigFile::CVar &needToCopy = cfg.getVar("NeedToCopy");
607 for (uint j = 0; j < needToCopy.size(); j++)
609 string arg1 = srcPath + needToCopy.asString(j);
610 string dstPath = CPath::standardizeDosPath(CPath::getFullPath(CPath::lookup (CFile::getFilename (needToCopy.asString(j))), false));
613 if(arg1.empty () || dstPath.empty ())
615 nlinfo ("Can't copy, src or dst is empty");
617 if(arg1 != dstPath)
619 nlinfo ("Copying '%s' into '%s'", arg1.c_str(), dstPath.c_str());
620 nlinfo ("executing 'copy /Y %s %s", arg1.c_str(), dstPath.c_str());
621 string str = "copy /Y " + arg1 + " " + dstPath;
622 int ret = system (str.c_str ());
623 //int ret = _spawnlp (_P_WAIT, "copy", "copy", "/Y", arg1.c_str(), dstPath.c_str(), NULL);
624 if (ret != 0)
626 nlwarning ("the copy command seems failed with the error code %d, errno %d: %s", ret, errno, strerror(errno));
629 else
631 nlinfo ("Can't copy, same path '%s'", arg1.c_str());
634 catch (const Exception &)
636 nlwarning ("Can't copy '%s' '%s', try the next file", arg1.c_str(), dstPath.c_str());
639 break;
642 catch (const Exception &e)
644 nlwarning (e.what ());
647 if (i == shards.size())
649 nlwarning ("the fsaddr '%s' is not in the shards.cfg, can't copy data_common files", fsaddr.asString().c_str());
653 catch (const Exception &)
655 nlinfo ("There's no shards.cfg, or bad file format, can't copy common files");
658 nlinfo ("CNET[%p]: Connecting to '%s' with cookie '%s'", this, _FrontendAddress.c_str(), _LoginCookie.toString().c_str ());
660 // then connect to the frontend using the udp sock
661 //ace faut faire la nouveau login client result = CLoginClient::connectToShard (_FrontendAddress, _Connection);
663 nlassert (!_Connection.connected());
668 // S12: connect to the FES. Note: In UDP mode, it's the user that have to send the cookie to the front end
670 _Connection.connect (CInetAddress(_FrontendAddress));
672 catch (const ESocket &e)
674 result = toString ("FS refused the connection (%s)", e.what());
675 return false;
678 _ConnectionState = Login;
680 _LatestLoginTime = ryzomGetLocalTime ();
681 _LatestSyncTime = _LatestLoginTime;
682 _LatestProbeTime = _LatestLoginTime;
683 m_LoginAttempts = 0;
685 nlinfo("CNET[%p]: Client connected to shard, attempting login", this);
686 return true;
689 void CNetworkConnection::setImpulseCallback(TImpulseCallback callback, void *argument)
691 _ImpulseCallback = callback;
692 _ImpulseArg = argument;
697 bool CNetworkConnection::isConnected()
699 return _ConnectionState == Connected;
702 #ifdef ENABLE_INCOMING_MSG_RECORDER
704 * Return true if there is some messages to replay (in replay mode)
706 bool CNetworkConnection::dataToReplayAvailable()
708 // Test the next tick loaded with the curent client tick (set externally)
709 //nldebug( "current=%u nextToReplay=%u", _CurrentClientTick, _NextClientTickToReplay );
710 return ( _CurrentClientTick >= _NextClientTickToReplay ); // when true => authorize entering buildStream()...
712 #endif
717 * Set MsPerTick value
719 void CNetworkConnection::setMsPerTick(sint32 msPerTick)
721 _MsPerTick = msPerTick;
730 bool CNetworkConnection::update()
732 #ifdef ENABLE_INCOMING_MSG_RECORDER
733 if ( _NextClientTickToReplay == std::numeric_limits<uint32>::max() )
735 setReplayingMode( false );
736 return false;
738 #endif
740 _UpdateTime = ryzomGetLocalTime ();
741 _UpdateTicks = ryzomGetPerformanceTime();
742 _ReceivedSync = false;
743 _NormalPacketsReceived = 0;
744 _TotalMessages = 0;
746 //nldebug("CNET[%d]: begin update()", this);
748 // If we are disconnected, bypass the real network update
749 if ( _ConnectionState == Disconnect )
751 _ConnectionQuality = false; // to block the user entity
752 return false;
755 // Yoyo. Update the Smooth ServerTick.
756 updateSmoothServerTick();
759 ////// MEASURE_FE_SENDING (special test mode, not used in normal mode)
760 #ifdef MEASURE_FE_SENDING
761 if ( _ConnectionState == Login )
763 //_Connection.setNonBlockingMode( true );
764 sendSystemLogin();
765 _ConnectionState = Connected;
768 // Receive
769 CBitMemStream msgin( true );
770 bool res = buildStream( msgin );
772 if ( res )
774 static sint32 loopcount = 0;
775 ++loopcount;
776 static TTicks lastdisplay = CTime::getPerformanceTime();
777 TTicks tn = CTime::getPerformanceTime();
778 TTime diff = CTime::ticksToSecond(tn - lastdisplay) * 1000.0;
779 if ( diff > 2000 )
781 nlinfo("Reads by second: %.1f => LoopTime = %.2f ms LoopCount = %u Diff = %u ms",(float)loopcount * 1000.0f / (float)diff, (float)diff / loopcount, loopcount, diff);
782 loopcount = 0;
783 lastdisplay = tn;
787 return res;
788 #endif
790 if (!_Connection.connected())
792 //if(!ClientCfg.Local)
793 // nlwarning("CNET[%p]: update() attempted whereas socket is not connected !", this);
794 return false;
799 // State automaton
800 bool stateBroke = false;
803 switch (_ConnectionState)
805 case Login:
806 // if receives System SYNC
807 // immediate state Synchronize
808 // else
809 // sends System LoginCookie
810 stateBroke = stateLogin();
811 break;
813 case Synchronize:
814 // if receives System PROBE
815 // immediate state Probe
816 // else if receives Normal
817 // immediate state Connected
818 // else
819 // sends System ACK_SYNC
820 stateBroke = stateSynchronize();
821 break;
823 case Connected:
824 // if receives System PROBE
825 // immediate state Probe
826 // else if receives Normal
827 // sends Normal data
828 stateBroke = stateConnected();
829 break;
831 case Probe:
832 // if receives System SYNC
833 // immediate state SYNC
834 // else if receives System PROBE
835 // decode PROBE
836 // sends System ACK_PROBE
837 stateBroke = stateProbe();
838 break;
840 case Stalled:
841 // if receives System SYNC
842 // immediate state SYNC
843 // else if receives System STALLED
844 // decode STALLED (nothing to do)
845 // else if receives System PROBE
846 // immediate state PROBE
847 stateBroke = stateStalled();
848 break;
850 case Quit:
851 // if receives System SYNC
852 // immediate state Synchronize
853 // else
854 // sends System LoginCookie
855 stateBroke = stateQuit();
856 break;
858 default:
859 // Nothing here !
860 stateBroke = false; // will come here if a disconnection action is received inside a method that returns true
861 break;
864 while (stateBroke);// && _TotalMessages<5);
866 catch (const ESocket &)
868 _ConnectionState = Disconnect;
871 //updateBufferizedPackets ();
873 PacketLossGraph.addOneValue (getMeanPacketLoss ());
875 _ConnectionQuality = (getConnectionState() == Connected &&
876 _UpdateTime - _LastReceivedNormalTime < 2000 && _CurrentClientTick < _CurrentServerTick);
878 return (_TotalMessages!=0);
883 * Receive available data and convert it to a bitmemstream
885 bool CNetworkConnection::buildStream( CBitMemStream &msgin )
887 #ifdef ENABLE_INCOMING_MSG_RECORDER
888 if ( _ReplayIncomingMessagesOn )
890 // Replay message
891 statsReceive( _NextMessageToReplay.length() );
892 msgin.clear();
893 memcpy( msgin.bufferToFill( _NextMessageToReplay.length() ), _NextMessageToReplay.buffer(), _NextMessageToReplay.length() );
894 //nldebug( "Reading message for tick %u (size %u)", _NextClientTickToReplay, msgin.length() );
896 // Preload next message
897 if ( _RecordedMessagesIn.eof() )
899 // Nothing more to load
900 _NextClientTickToReplay = std::numeric_limits<uint32>::max();
901 nlinfo( "Nothing more to replay, end of replaying" );
903 else
905 _RecordedMessagesIn.serial( _NextClientTickToReplay );
906 _NextMessageToReplay.clear();
907 _RecordedMessagesIn.serialMemStream( _NextMessageToReplay );
910 return true;
912 #endif
914 uint32 len = 65536;
915 if ( _Connection.receive( (uint8*)_ReceiveBuffer, len, false ) )
917 // Compute some statistics
918 statsReceive( len );
920 // Fill the message
921 msgin.clear();
922 memcpy( msgin.bufferToFill( len ), _ReceiveBuffer, len );
924 #ifdef ENABLE_INCOMING_MSG_RECORDER
925 if ( _RecordIncomingMessagesOn )
927 _RecordedMessagesOut.serial( _CurrentClientTick );
928 _RecordedMessagesOut.serialMemStream( msgin ); // shouldn't msgin's bufpos be resetted?
930 #endif
931 return true;
933 else
935 // A receiving error means the front-end is down
936 _ConnectionState = Disconnect;
937 disconnect(); // won't send a disconnection msg because state is already Disconnect
938 nlwarning( "DISCONNECTION" );
939 return false;
947 // Client automaton states methods
954 // Login state
958 // sends system login cookie
959 void CNetworkConnection::sendSystemLogin()
961 CBitMemStream message;
963 buildSystemHeader(message);
965 uint8 login = SYSTEM_LOGIN_CODE;
966 message.serial(login);
968 if (_LoginCookie.isValid())
969 message.serial(_LoginCookie);
970 else
972 uint32 fakeCookie = 0xDEADBEEF;
973 message.serial(fakeCookie);
974 message.serial(fakeCookie);
975 message.serial(fakeCookie);
977 message.serial( ClientCfg.LanguageCode );
979 // Try to send login, and handle the case when a firewall blocks the sending
980 uint32 length = message.length();
981 static TTime attemptStartTime = CTime::getLocalTime();
984 //sendUDP (&(_Connection), message.buffer(), length);
985 _Connection.send( message.buffer(), length );
987 catch (const ESocket &e)
989 #ifdef NL_OS_WINDOWS
990 // An exception (10004: Blocking operation interrupted) may occur if a firewall such as Kerio is
991 // running (note: ZoneAlarm blocks connect() until a decision is made by the user).
992 // Handle true network errors with a nlerror dialog box
993 if ( string(e.what()).find( "10004:" ) == string::npos ) // Code of WSAEINTR
995 // nlerror( "Cannot login: %s", e.what() );
997 #endif
998 // The first time, display info for the user to configure his personal firewall
999 static bool exceptionThrown = false;
1000 if ( ! exceptionThrown )
1002 exceptionThrown = true;
1003 throw EBlockedByFirewall();
1006 // Next time, disconnect if the time-out expired
1007 //nldebug( "Attempt interrupted at %u ms", (uint32)(CTime::getLocalTime()-attemptStartTime) );
1008 TTime currentTime = CTime::getLocalTime();
1009 if ( currentTime - attemptStartTime > 15000 ) // let 15 seconds for the user to allow the connection
1011 nldebug( "Login failed at %u ms", (uint32)(CTime::getLocalTime()-attemptStartTime) );
1012 //nlerror( "Cannot login (check your firewall's settings?): %s", e.what() );
1013 throw EBlockedByFirewall();
1017 statsSend( length );
1018 nlinfo( "CNET[%p]: sent LOGIN cookie=%s", this, _LoginCookie.toString().c_str() );
1019 //nlinfo( "CNET[%p]: sent LOGIN cookie=%s at attempt %u at %u ms", this, _LoginCookie.toString().c_str(), nbAttempts, (uint32)(CTime::getLocalTime()-attemptStartTime) );
1022 bool CNetworkConnection::stateLogin()
1024 // if receives System SYNC
1025 // immediate state Synchronize
1026 // else
1027 // sends System LoginCookie
1029 #ifdef ENABLE_INCOMING_MSG_RECORDER
1030 if ( ClientCfg.Local && !_ReplayIncomingMessagesOn )
1031 return false;
1032 while ( (_ReplayIncomingMessagesOn && dataToReplayAvailable()) ||
1033 _Connection.dataAvailable() )
1034 #else
1035 while ( _Connection.dataAvailable() )// && _TotalMessages<5)
1036 #endif
1038 _DecodedHeader = false;
1039 CBitMemStream msgin (true);
1040 if (buildStream(msgin) && decodeHeader(msgin))
1042 if (_SystemMode)
1044 uint8 message = 0;
1045 msgin.serial(message);
1047 switch (message)
1049 case SYSTEM_SYNC_CODE:
1050 // receive sync, decode sync
1051 _ConnectionState = Synchronize;
1052 nldebug("CNET[%p]: login->synchronize", this);
1053 receiveSystemSync(msgin);
1054 return true;
1055 break;
1056 case SYSTEM_STALLED_CODE:
1057 // receive stalled, decode stalled and state stalled
1058 _ConnectionState = Stalled;
1059 nldebug("CNET[%p]: login->stalled", this);
1060 receiveSystemStalled(msgin);
1061 return true;
1062 break;
1063 case SYSTEM_PROBE_CODE:
1064 // receive probe, decode probe and state probe
1065 _ConnectionState = Probe;
1066 _Changes.push_back(CChange(0, ProbeReceived));
1067 nldebug("CNET[%p]: login->probe", this);
1068 receiveSystemProbe(msgin);
1069 return true;
1070 break;
1071 case SYSTEM_SERVER_DOWN_CODE:
1072 disconnect(); // will send disconnection message
1073 nlwarning( "BACK-END DOWN" );
1074 return false; // exit now from loop, don't expect a new state
1075 break;
1076 default:
1077 //msgin.displayStream("DBG:BEN:stateLogin:msgin");
1078 nlwarning("CNET[%p]: received system %d in state Login", this, message);
1079 break;
1082 else
1084 //msgin.displayStream("DBG:BEN:stateLogin:msgin");
1085 nlwarning("CNET[%p]: received normal in state Login", this);
1090 // send ack sync if received sync or last sync timed out
1091 if (_UpdateTime-_LatestLoginTime > 300)
1093 sendSystemLogin();
1094 _LatestLoginTime = _UpdateTime;
1095 if (m_LoginAttempts > 24)
1097 m_LoginAttempts = 0;
1098 disconnect(); // will send disconnection message
1099 nlwarning("CNET[%p]: Too many LOGIN attempts, connection problem", this);
1100 return false; // exit now from loop, don't expect a new state
1102 else
1104 ++m_LoginAttempts;
1108 return false;
1114 // Sync state
1117 void CNetworkConnection::receiveSystemSync(CBitMemStream &msgin)
1119 #ifdef ENABLE_INCOMING_MSG_RECORDER
1120 if ( _ReplayIncomingMessagesOn )
1122 TGameCycle dummyTick;
1123 TTime dummyTime;
1124 msgin.serial( dummyTick );
1125 msgin.serial( dummyTime );
1126 return;
1128 #endif
1130 _LatestSyncTime = _UpdateTime;
1131 TTime stime;
1132 msgin.serial(_Synchronize);
1133 msgin.serial(stime);
1134 msgin.serial(_LatestSync);
1136 if (CheckXMLSignature.get())
1138 bool xmlInvalid = false;
1140 CHashKeyMD5 checkMsgXml;
1141 CHashKeyMD5 checkDatabaseXml;
1145 msgin.serialBuffer(checkMsgXml.Data, sizeof(checkMsgXml.Data));
1146 msgin.serialBuffer(checkDatabaseXml.Data, sizeof(checkDatabaseXml.Data));
1148 // Since cannot now easily if the server run on Windows or unix, try the both methods
1149 xmlInvalid = (checkMsgXml != _MsgXmlMD5 || checkDatabaseXml != _DatabaseXmlMD5);
1150 if(xmlInvalid)
1151 xmlInvalid = (checkMsgXml != _AltMsgXmlMD5 || checkDatabaseXml != _AltDatabaseXmlMD5);
1153 catch (const NLMISC::Exception&)
1157 static bool alreadyWarned = false;
1158 if (xmlInvalid && !alreadyWarned)
1160 alreadyWarned = true;
1161 Driver->systemMessageBox("msg.xml and database.xml files are invalid (server version signature is different)", "XML files invalid");
1163 nlwarning("XML signature is invalid:");
1164 nlwarning("msg.xml client:%s,%s server:%s", _AltMsgXmlMD5.toString().c_str(), _MsgXmlMD5.toString().c_str(),
1165 checkMsgXml.toString().c_str());
1166 nlwarning("database.xml client:%s,%s server:%s", _AltDatabaseXmlMD5.toString().c_str(), _DatabaseXmlMD5.toString().c_str(),
1167 checkDatabaseXml.toString().c_str());
1171 _ReceivedSync = true;
1173 setMsPerTick(100);
1174 //_MsPerTick = 100; // initial values
1176 #ifdef HALF_FREQUENCY_SENDING_TO_CLIENT
1177 //#pragma message ("HALF_FREQUENCY_SENDING_TO_CLIENT")
1178 _CurrentServerTick = _Synchronize+_CurrentReceivedNumber*2;
1179 #else
1180 //#pragma message ("FULL_FREQUENCY_SENDING_TO_CLIENT")
1181 _CurrentServerTick = _Synchronize+_CurrentReceivedNumber;
1182 #endif
1183 _CurrentClientTick = uint32(_CurrentServerTick - (_LCT+_MsPerTick)/_MsPerTick);
1184 _CurrentClientTime = _UpdateTime - (_LCT+_MsPerTick);
1186 //nlinfo( "CNET[%p]: received SYNC %" NL_I64 "u %" NL_I64 "u - _CurrentReceivedNumber=%d _CurrentServerTick=%d", this, (uint64)_Synchronize, (uint64)stime, _CurrentReceivedNumber, _CurrentServerTick );
1188 sendSystemAckSync();
1191 // sends system sync acknowledge
1192 void CNetworkConnection::sendSystemAckSync()
1194 #ifdef ENABLE_INCOMING_MSG_RECORDER
1195 if ( _ReplayIncomingMessagesOn )
1196 return;
1197 #endif
1199 CBitMemStream message;
1201 buildSystemHeader(message);
1203 uint8 sync = SYSTEM_ACK_SYNC_CODE;
1204 message.serial(sync);
1205 message.serial(_LastReceivedNumber);
1206 message.serial(_LastAckInLongAck);
1207 message.serial(_LongAckBitField);
1208 message.serial(_LatestSync);
1210 uint32 length = message.length();
1211 _Connection.send (message.buffer(), length);
1212 //sendUDP (&(_Connection), message.buffer(), length);
1213 statsSend(length);
1215 _LatestSyncTime = _UpdateTime;
1217 //nlinfo("CNET[%p]: sent ACK_SYNC, _LastReceivedNumber=%d _LastAckInLongAck=%d", this, _LastReceivedNumber, _LastAckInLongAck);
1219 /* // display long ack
1220 uint i;
1221 uint bfsize = _LongAckBitField.size();
1222 uint bbuffer = 0;
1223 string buffer;
1224 static const char htable[] = "0123456789ABCDEF";
1225 for (i=0; i<bfsize; ++i)
1227 if (i>0 && (i&3)==0)
1229 buffer += htable[bbuffer];
1230 bbuffer = 0;
1233 bbuffer = bbuffer*2 + (_LongAckBitField.get((_LastReceivedNumber-i)&(bfsize-1)) ? 1 : 0);
1236 buffer += htable[bbuffer];
1237 nlinfo("CNET[%p]: ACK=%s", buffer.c_str());
1240 bool CNetworkConnection::stateSynchronize()
1242 // if receives System PROBE
1243 // immediate state Probe
1244 // else if receives Normal
1245 // immediate state Connected
1246 // sends System ACK_SYNC
1247 #ifdef ENABLE_INCOMING_MSG_RECORDER
1248 if ( ClientCfg.Local && !_ReplayIncomingMessagesOn )
1249 return false;
1250 while ( (_ReplayIncomingMessagesOn && dataToReplayAvailable()) ||
1251 _Connection.dataAvailable() )
1252 #else
1253 while (_Connection.dataAvailable())// && _TotalMessages<5)
1254 #endif
1256 _DecodedHeader = false;
1257 CBitMemStream msgin (true);
1258 if (buildStream(msgin) && decodeHeader(msgin))
1260 if (_SystemMode)
1262 uint8 message = 0;
1263 msgin.serial(message);
1265 switch (message)
1267 case SYSTEM_PROBE_CODE:
1268 // receive probe, decode probe and state probe
1269 _ConnectionState = Probe;
1270 //nldebug("CNET[%p]: synchronize->probe", this);
1271 _Changes.push_back(CChange(0, ProbeReceived));
1272 receiveSystemProbe(msgin);
1273 return true;
1274 break;
1275 case SYSTEM_STALLED_CODE:
1276 // receive stalled, decode stalled and state stalled
1277 _ConnectionState = Stalled;
1278 //nldebug("CNET[%p]: synchronize->stalled", this);
1279 receiveSystemStalled(msgin);
1280 return true;
1281 break;
1282 case SYSTEM_SYNC_CODE:
1283 // receive sync, decode sync
1284 receiveSystemSync(msgin);
1285 break;
1286 case SYSTEM_SERVER_DOWN_CODE:
1287 disconnect(); // will send disconnection message
1288 nlwarning( "BACK-END DOWN" );
1289 return false; // exit now from loop, don't expect a new state
1290 break;
1291 default:
1292 nlwarning("CNET[%p]: received system %d in state Synchronize", this, message);
1293 break;
1296 else
1298 _ConnectionState = Connected;
1299 //nlwarning("CNET[%p]: synchronize->connected", this);
1300 _Changes.push_back(CChange(0, ConnectionReady));
1301 _ImpulseDecoder.reset();
1302 receiveNormalMessage(msgin);
1303 return true;
1308 // send ack sync if received sync or last sync timed out
1309 if (_UpdateTime-_LatestSyncTime > 300)
1310 sendSystemAckSync();
1312 return false;
1316 #ifdef SHOW_PROPERTIES_RECEIVED
1317 uint8 propReceived [18];
1318 #endif
1319 #ifdef MEASURE_RECEIVE_DATES
1320 TTime currentTime;
1321 #endif
1324 // Connected state
1327 void CNetworkConnection::receiveNormalMessage(CBitMemStream &msgin)
1329 //nlinfo("CNET[%p]: received normal message Packet=%d Ack=%d", this, _LastReceivedNumber, _LastReceivedAck);
1331 vector<CAction *> actions;
1332 _ImpulseDecoder.decode(msgin, _CurrentReceivedNumber, _LastReceivedAck, _CurrentSendNumber, actions);
1333 #ifdef SHOW_PROPERTIES_RECEIVED
1334 for ( uint8 p=0; p!=18; ++p )
1335 propReceived[p] = 0;
1336 propReceived[2] = actions.size();
1337 #endif
1339 ++_NormalPacketsReceived;
1342 // we can now remove all old action that are acked
1343 while (!_Actions.empty() && _Actions.front().FirstPacket != 0 && _Actions.front().FirstPacket <= _LastReceivedAck)
1345 // warning, CActionBlock automatically remove() actions when deleted
1346 _Actions.pop_front();
1349 // now, read actions
1350 /*vector<CAction *> commonActions;
1351 CActionFactory::getInstance()->unpack (msgin, commonActions, getCurrentServerTick()+1 ); // +1 because the current tick is set a few lines further
1353 actions.insert(actions.end(), commonActions.begin(), commonActions.end());*/
1355 //_PropertyDecoder.ack(_LastReceivedNumber, _LastReceivedAck);
1359 // update game time and ticks from network infos
1362 #ifdef ENABLE_INCOMING_MSG_RECORDER
1363 if ( ! _ReplayIncomingMessagesOn )
1364 #endif
1366 // convert the number of the packet that we just received into tick
1367 #ifdef HALF_FREQUENCY_SENDING_TO_CLIENT
1368 nlassert(_CurrentReceivedNumber*2+_Synchronize > _CurrentServerTick);
1369 _CurrentServerTick = _CurrentReceivedNumber*2+_Synchronize;
1370 #else
1371 nlassert(_CurrentReceivedNumber+_Synchronize > _CurrentServerTick);
1372 _CurrentServerTick = _CurrentReceivedNumber+_Synchronize;
1373 #endif
1374 //nldebug( "_CurrentServerTick=%d _CurrentReceivedNumber=%d _Synchronize=%d", _CurrentServerTick, _CurrentReceivedNumber, _Synchronize );
1377 // remove useless stamps in queue
1378 while (!_PacketStamps.empty() && _LastReceivedAck > _PacketStamps.front().first)
1379 _PacketStamps.pop_front();
1381 #ifdef ENABLE_INCOMING_MSG_RECORDER
1382 if ( (! _ReplayIncomingMessagesOn) && (_PacketStamps.empty() || _PacketStamps.front().first > _LastReceivedAck) )
1383 #else
1384 if (_PacketStamps.empty() || _PacketStamps.front().first > _LastReceivedAck)
1385 #endif
1387 //nlwarning("Frontend ack'ed message %d not stamp dated", _LastReceivedAck);
1389 else
1391 // get the send time of the acknowledged packet
1392 TTime ackedPacketTime = _PacketStamps.front().second;
1394 // update ping
1395 uint32 ping = (uint32)(_UpdateTime-ackedPacketTime);
1396 _InstantPing = ping;
1397 if (ping < _BestPing)
1398 _BestPing = ping;
1400 PingGraph.addOneValue (float(ping));
1402 // earliest estimation of server packet send time and latest estimation (based on ping and acknowledge)
1403 TTime earliest = ackedPacketTime + _BestPing/2,
1404 latest = _UpdateTime - _BestPing/2;
1406 // compute number of ticks between frame currently played by client and packet received from server
1407 sint32 numStepTick = (sint32)(_CurrentServerTick-_CurrentClientTick);
1409 // if enough steps and times are valid
1410 if (numStepTick > 0 && earliest > _CurrentClientTime && latest > _CurrentClientTime)
1412 // exact formula for _MsPerTick = (_CurrentServerTime-_CurrentClientTime)/numStepTick
1413 // where _CurrentServerTime is the actual server packet send time
1414 // but as the exact time is unknown, we use a predictive time window instead, based on
1415 // the acknwoledged packet send time, the received packet time and the ping
1417 // adjust if estimation of _MsPerTick is too small
1418 if ((TTime)(_CurrentClientTime+_MsPerTick*numStepTick) < earliest)
1419 setMsPerTick((sint32)(earliest-_CurrentClientTime)/numStepTick);
1420 //_MsPerTick = (sint32)(earliest-_CurrentClientTime)/numStepTick;
1422 // adjust if estimation of _MsPerTick is too large
1423 if ((TTime)(_CurrentClientTime+_MsPerTick*numStepTick) > latest)
1424 setMsPerTick((sint32)(latest-_CurrentClientTime)/numStepTick);
1425 //_MsPerTick = (sint32)(latest-_CurrentClientTime)/numStepTick;
1427 // _MsPerTick should be positive here -- seems to crash yet
1428 /// \todo we should instead of putting 1, returning in probe mode because it means that we had a very big lag
1429 if (_MsPerTick == 0)
1431 nlwarning ("_MsPerTick is 0 because server tick is too big %d compare to the client tick is %d", _CurrentServerTick, _CurrentClientTick);
1432 setMsPerTick(1);
1433 //_MsPerTick = 1;
1436 else if (numStepTick <= 0)
1438 setMsPerTick((sint32)_LCT);
1439 //_MsPerTick = (sint32)_LCT;
1441 MsPerTickGraph.addOneValue (float(_MsPerTick));
1445 #ifdef MEASURE_RECEIVE_DATES
1446 currentTime = ryzomGetLocalTime ();
1447 #endif
1449 // Decode the actions received in the impulsions
1450 uint i;
1451 for (i = 0; i < actions.size (); i++)
1453 switch (actions[i]->Code)
1456 case ACTION_DISCONNECTION_CODE:
1458 // Self disconnection
1459 nlwarning( "You were disconnected by the server" );
1460 disconnect(); // will send disconnection message
1461 LoginSM.pushEvent( CLoginStateMachine::ev_conn_dropped );
1463 break;
1464 case ACTION_GENERIC_CODE:
1466 genericAction((CActionGeneric *)actions[i]);
1468 break;
1469 case ACTION_GENERIC_MULTI_PART_CODE:
1471 genericAction((CActionGenericMultiPart *)actions[i]);
1473 break;
1474 case ACTION_DUMMY_CODE:
1476 CActionDummy *dummy = ((CActionDummy*)actions[i]);
1477 nldebug("CNET[%d] Received Dummy %d", this, dummy->Dummy1);
1478 // Nothing to do
1480 break;
1483 CActionFactory::getInstance()->remove(actions[i]);
1487 // Decode the visual properties
1488 decodeVisualProperties( msgin );
1490 _LastReceivedNormalTime = _UpdateTime;
1492 #ifdef DISPLAY_ENTITIES
1493 DownloadGraph.addValue ((float)(msgin.length()));
1494 DpfGraph.addValue ((float)(msgin.length()));
1495 #endif
1496 #ifdef SHOW_PROPERTIES_RECEIVED
1498 string str = "Received: ";
1499 // stringstream ss;
1500 // ss << "Received: ";
1501 if ( propReceived[2] != 0 )
1502 str += NLMISC::toString(propReceived[2]) + " impuls. ";
1503 // ss << propReceived[2] << " impuls. ";
1504 if ( propReceived[0] != 0 )
1505 str += NLMISC::toString(propReceived[0]) + " pos; ";
1506 // ss << propReceived[0] << " pos; ";
1507 if ( propReceived[3] != 0 )
1508 str += NLMISC::toString(propReceived[3]) + " orient; ";
1509 // ss << propReceived[3] << " orient; ";
1510 uint sum = propReceived[4] + propReceived[5] + propReceived[6] + propReceived[7] + propReceived[8] + propReceived[9];
1511 if ( sum != 0 )
1512 str += NLMISC::toString(sum) + " discreet; ";
1513 // ss << sum << " discreet; ";
1514 if ( propReceived[16] != 0 )
1515 str += NLMISC::toString(propReceived[16]) + "assoc; ";
1516 // ss << propReceived[16] << "assoc; ";
1517 if ( propReceived[17] != 0 )
1518 str += NLMISC::toString(propReceived[17]) + "disac; ";
1519 // ss << propReceived[17] << "disac; ";
1520 str += "TOTAL: " + NLMISC::toString(propReceived[2]) + " + " + NLMISC::toString(propReceived[0] + propReceived[3] + sum);
1521 //ss << "TOTAL: " << propReceived[2] << " + " << propReceived[0] + propReceived[3] + sum;
1522 nlwarning( "%s", str.c_str() );
1523 #endif
1527 void CNetworkConnection::decodeVisualProperties( CBitMemStream& msgin )
1531 //nldebug( "pos: %d len: %u", msgin.getPos(), msgin.length() );
1532 while ( true )
1534 //nlinfo( "Reading pass %u, BEFORE HEADER: pos: %d len: %u", ++i, msgin.getPosInBit(), msgin.length() * 8 );
1536 // Check if there is a new block to read
1537 if ( msgin.getPosInBit() + (sizeof(TCLEntityId)*8) > msgin.length()*8 )
1538 return;
1542 // Header
1543 TCLEntityId slot;
1544 msgin.serialAndLog1( slot );
1546 uint32 associationBits;
1547 msgin.serialAndLog2( associationBits, 2 );
1548 //nlinfo( "slot %hu AB: %u", (uint16)slot, associationBits );
1549 if ( associationBitsHaveChanged( slot, associationBits ) && (!IgnoreEntityDbUpdates || slot==0))
1551 //displayBitStream( msgin, beginbitpos, msgin.getPosInBit() );
1552 // nlinfo ("Disassociating S%hu (AB %u)", (uint16)slot, associationBits );
1553 if ( _PropertyDecoder.isUsed( slot ) )
1555 TSheetId sheet = _PropertyDecoder.sheet( slot );
1556 TIdMap::iterator it = _IdMap.find( sheet );
1557 if ( it != _IdMap.end() )
1558 _IdMap.erase(it);
1559 _PropertyDecoder.removeEntity( slot );
1561 CChange theChange( slot, RemoveOldEntity );
1562 _Changes.push_back( theChange );
1564 else
1566 // nlinfo( "Cannot disassociate slot %hu: sheet not received yet", (uint16)slot );
1570 // Read the timestamp delta if there's one (otherwise take _CurrentServerTick)
1571 TGameCycle timestamp;
1572 bool timestampIsThere;
1573 msgin.serialBitAndLog( timestampIsThere );
1574 if ( timestampIsThere )
1576 uint32 timestampDelta;
1577 msgin.serialAndLog2( timestampDelta, 4 );
1578 timestamp = _CurrentServerTick - timestampDelta;
1579 //nldebug( "TD: %u (S%hu)", timestampDelta, (uint16)slot );
1581 else
1583 timestamp = _CurrentServerTick;
1586 // Tree
1587 //nlinfo( "AFTER HEADER: posBit: %d pos: %d len: %u", msgin.getPosInBit(), msgin.getPos(), msgin.length() );
1589 TVPNodeClient *currentNode = _VisualPropertyTreeRoot;
1590 msgin.serialBitAndLog( currentNode->a()->BranchHasPayload );
1591 if ( currentNode->a()->BranchHasPayload )
1593 CActionPosition *ap = (CActionPosition*)CActionFactory::getInstance()->create( slot, ACTION_POSITION_CODE );
1594 ap->unpack( msgin );
1595 _PropertyDecoder.receive( _CurrentReceivedNumber, ap );
1596 #ifdef SHOW_PROPERTIES_RECEIVED
1597 ++propReceived[PROPERTY_POSITION];
1598 #endif
1601 * Set into property database
1604 // TEMP
1605 if ( ap->Position[0]==0 || ap->Position[1]==0 )
1606 nlwarning( "S%hu: Receiving an invalid position", (uint16)slot );
1608 if (_DataBase != NULL && (!IgnoreEntityDbUpdates || slot==0))
1610 CCDBNodeBranch *nodeRoot;
1611 nodeRoot = dynamic_cast<CCDBNodeBranch*>(_DataBase->getNode((uint16)0));
1612 if(nodeRoot)
1614 CCDBNodeLeaf *node;
1615 node = dynamic_cast<CCDBNodeLeaf*>(nodeRoot->getNode(slot)->getNode(0));
1616 nlassert(node != NULL);
1617 node->setValue64(ap->Position[0]);
1618 node = dynamic_cast<CCDBNodeLeaf*>(nodeRoot->getNode(slot)->getNode(1));
1619 nlassert(node != NULL);
1620 node->setValue64(ap->Position[1]);
1621 node = dynamic_cast<CCDBNodeLeaf*>(nodeRoot->getNode(slot)->getNode(2));
1622 nlassert(node != NULL);
1623 node->setValue64(ap->Position[2]);
1625 if ( LoggingMode )
1627 nlinfo( "recvd position (%d,%d) for slot %hu, date %u", (sint32)(ap->Position[0]), (sint32)(ap->Position[1]), (uint16)slot, timestamp );
1632 bool interior = ap->Interior;
1634 CActionFactory::getInstance()->remove( (CAction*&)ap );
1638 * Statistical prediction of time before next position update: set PredictedInterval
1641 //nlassert( MAX_POSUPDATETICKQUEUE_SIZE > 1 );
1642 deque<TGameCycle>& puTicks = _PosUpdateTicks[slot];
1643 multiset<TGameCycle>& puIntervals = _PosUpdateIntervals[slot];
1645 // Flush the old element of tick queue and of the interval sorted set
1646 if ( puTicks.size() == MAX_POSUPDATETICKQUEUE_SIZE )
1648 puIntervals.erase( puIntervals.find( puTicks[1] - puTicks[0] ) ); // erase only one element, not all corresponding to the value
1649 puTicks.pop_front();
1652 // Add a new element to the tick queue and possibly to the interval sorted set
1653 // Still to choose: _CurrentServerTick or timestamp ?
1654 TGameCycle latestInterval = 0;
1655 if ( ! puTicks.empty() )
1657 latestInterval = timestamp - puTicks.back();
1658 puIntervals.insert( latestInterval );
1660 if ( PosUpdateIntervalGraph )
1661 PosUpdateIntervalGraph->addOneValue( slot, (float)latestInterval );
1663 puTicks.push_back( timestamp );
1665 nlassert( puTicks.size() == puIntervals.size()+1 );
1667 // Prediction function : Percentile(25 last, 0.8) + 1
1668 TGameCycle predictedInterval;
1669 if ( puIntervals.empty() )
1671 predictedInterval = 0;
1673 else
1675 predictedInterval = (TGameCycle)(percentileRev( puIntervals, PREDICTION_REV_PERCENTILE ) + 1);
1677 //if ( predictedInterval > 100 )
1678 // nlwarning( "Slot %hu: Predicted interval %u exceeds 100 ticks", (uint16)slot, predictedInterval );
1680 if ( PosUpdatePredictionGraph )
1681 PosUpdatePredictionGraph->addOneValue( slot, (float)predictedInterval );
1684 //nlinfo( "Slot %hu: Interval=%u Predicted=%u", (uint16)slot, latestInterval, predictedInterval );
1687 * Add into the changes vector
1689 CChange thechange( slot, PROPERTY_POSITION, timestamp );
1690 thechange.PositionInfo.PredictedInterval = predictedInterval;
1691 thechange.PositionInfo.IsInterior = interior;
1692 _Changes.push_back( thechange );
1694 #ifdef MEASURE_RECEIVE_DATES
1695 // Stat log
1696 if ( LogReceiveEnabled && (WatchedEntitySlot == 256) || (WatchedEntitySlot == slot) )
1698 TRDateState ds( timestamp, predictedInterval, currentTime );
1699 ReceivePosDateLog[slot].push_back( ds );
1701 #endif
1705 currentNode = currentNode->b();
1706 msgin.serialBitAndLog( currentNode->BranchHasPayload );
1707 if ( currentNode->BranchHasPayload )
1709 msgin.serialBitAndLog( currentNode->a()->BranchHasPayload );
1710 if ( currentNode->a()->BranchHasPayload )
1712 CActionSint64 *ac = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( slot, PROPERTY_ORIENTATION );
1713 ac->unpack( msgin );
1715 // Process orientation
1716 CChange thechange(slot, PROPERTY_ORIENTATION, timestamp);
1717 _Changes.push_back( thechange );
1718 #ifdef SHOW_PROPERTIES_RECEIVED
1719 ++propReceived[PROPERTY_ORIENTATION];
1720 #endif
1721 if (_DataBase != NULL && (!IgnoreEntityDbUpdates || slot==0))
1723 CCDBNodeBranch *nodeRoot;
1724 nodeRoot = dynamic_cast<CCDBNodeBranch*>(_DataBase->getNode(0));
1725 if ( nodeRoot )
1727 CCDBNodeLeaf *node = dynamic_cast<CCDBNodeLeaf*>(nodeRoot->getNode(slot)->getNode( PROPERTY_ORIENTATION ));
1728 nlassert(node != NULL);
1729 node->setValue64(ac->getValue());
1730 if ( LoggingMode )
1732 nlinfo( "CLIENT: recvd property %hu (%s) for slot %hu, date %u", (uint16)PROPERTY_ORIENTATION, getPropText(PROPERTY_ORIENTATION), (uint16)slot, timestamp );
1734 //nldebug("CLPROPNET[%p]: received property %d for entity %d: %" NL_I64 "u", this, action->PropIndex, action->CLEntityId, action->getValue());
1738 CActionFactory::getInstance()->remove( (CAction*&)ac );
1741 TVPNodeClient::SlotContext.NetworkConnection = this;
1742 TVPNodeClient::SlotContext.Slot = slot;
1743 TVPNodeClient::SlotContext.Timestamp = timestamp;
1745 // Discreet properties
1746 currentNode->b()->decodeDiscreetProperties( msgin );
1750 catch (const EStreamOverflow&)
1752 // End of stream (saves useless bits)
1760 static vector<TCLEntityId> TargetSlotsList(256);
1762 void CNetworkConnection::decodeDiscreetProperty( CBitMemStream& msgin, TPropIndex propIndex )
1764 //nldebug( "Reading discreet property %hu at bitpos %d", (uint16)propIndex, msgin.getPosInBit() );
1766 TCLEntityId slot = TVPNodeClient::SlotContext.Slot;
1768 // \todo BEN this is temp, put it somewhere in database
1769 if (propIndex == PROPERTY_TARGET_LIST)
1771 uint8 listSize;
1772 msgin.serial(listSize);
1774 TargetSlotsList.resize(listSize);
1775 if (listSize > 0)
1776 msgin.serialBuffer(&(TargetSlotsList[0]), listSize);
1778 // Set target list value in database
1779 if (_DataBase != NULL && (!IgnoreEntityDbUpdates || slot==0))
1781 CCDBNodeBranch *nodeRoot;
1782 nodeRoot = dynamic_cast<CCDBNodeBranch*>(_DataBase->getNode(0));
1783 if ( nodeRoot )
1785 CCDBNodeBranch *nodeEntity = dynamic_cast<CCDBNodeBranch*>(nodeRoot->getNode(slot));
1786 nlassert(nodeEntity != NULL);
1788 uint writeProp = PROPERTY_TARGET_LIST;
1789 uint place = 0;
1791 if (listSize >= 32)
1793 listSize = 32;
1795 else
1797 TargetSlotsList.push_back(INVALID_SLOT);
1798 ++listSize;
1801 CCDBNodeLeaf *nodeProp = NULL;
1803 uint i;
1804 uint64 value = 0;
1805 for (i=0; i<listSize; ++i)
1807 if (place == 0)
1808 value = 0;
1810 value += (((uint64)TargetSlotsList[i]) << (place*8));
1812 ++place;
1813 if (place == 8)
1815 nodeProp = dynamic_cast<CCDBNodeLeaf*>(nodeEntity->getNode(writeProp));
1816 nlassert(nodeProp != NULL);
1817 nodeProp->setValue64(value);
1818 ++writeProp;
1819 place = 0;
1823 if (place != 0)
1825 nodeProp = dynamic_cast<CCDBNodeLeaf*>(nodeEntity->getNode(writeProp));
1826 nlassert(nodeProp != NULL);
1827 nodeProp->setValue64(value);
1831 if ( LoggingMode )
1833 nlinfo( "CLIENT: recvd property %hu (%s) for slot %hu, date %u", (uint16)propIndex, getPropText(propIndex), (uint16)slot, TVPNodeClient::SlotContext.Timestamp );
1837 CChange thechange( slot, propIndex, TVPNodeClient::SlotContext.Timestamp );
1838 _Changes.push_back( thechange );
1840 return;
1843 CActionSint64 *ac = (CActionSint64*)CActionFactory::getInstance()->createByPropIndex( slot, propIndex );
1844 ac->unpack( msgin );
1846 #ifdef SHOW_PROPERTIES_RECEIVED
1847 ++propReceived[propIndex];
1848 #endif
1850 switch ( propIndex )
1852 case PROPERTY_SHEET:
1854 // Special case for sheet
1855 // nlinfo ("Associating S%hu", (uint16)slot );
1856 if ( _PropertyDecoder.isUsed( slot ) )
1858 TSheetId sheet = _PropertyDecoder.sheet( slot );
1859 TIdMap::iterator it = _IdMap.find(sheet);
1860 if ( it != _IdMap.end() )
1861 _IdMap.erase(it);
1862 _PropertyDecoder.removeEntity( slot );
1865 TSheetId newSheetId = (TSheetId)(ac->getValue() & 0xffffffff);
1866 _IdMap.insert( make_pair( newSheetId, slot) );
1867 _PropertyDecoder.addEntity( slot, newSheetId );
1869 // Reset the position update statistical data
1870 _PosUpdateTicks[slot].clear();
1871 _PosUpdateIntervals[slot].clear();
1873 // Read optional alias block
1874 uint32 alias = 0;
1875 bool aliasBit = false;
1876 msgin.serialBitAndLog( aliasBit );
1877 if ( aliasBit )
1878 msgin.serialAndLog1( alias );
1880 // Set information
1881 CChange thechange( slot, AddNewEntity );
1882 thechange.NewEntityInfo.DataSetIndex = (TClientDataSetIndex)((ac->getValue() >> 32) & 0xffffffff);
1883 thechange.NewEntityInfo.Alias = alias;
1884 _Changes.push_back( thechange );
1885 break;
1887 case PROPERTY_MODE:
1889 // Special case for mode: push theta or pos, then mode
1890 uint64 mode44 = ac->getValue();
1891 uint32 modeTimestamp = _CurrentServerTick - (uint32)(((mode44 >> 8) & 0xF));
1893 // Push the mode Before the position or the orientation
1894 CChange thechangeMode( slot, PROPERTY_MODE, modeTimestamp );
1895 _Changes.push_back( thechangeMode );
1897 // Set mode value in database
1898 if (_DataBase != NULL && (!IgnoreEntityDbUpdates || slot==0))
1900 CCDBNodeBranch *nodeRoot;
1901 nodeRoot = dynamic_cast<CCDBNodeBranch*>(_DataBase->getNode(0));
1902 if ( nodeRoot )
1904 CCDBNodeLeaf *node = dynamic_cast<CCDBNodeLeaf*>(nodeRoot->getNode(slot)->getNode( propIndex ));
1905 nlassert(node != NULL);
1906 node->setValue64(ac->getValue() & 0xFF); // (8 bits)
1907 if ( LoggingMode )
1909 nlinfo( "CLIENT: recvd property %hu (%s) for slot %hu, date %u", (uint16)propIndex, getPropText(propIndex), (uint16)slot, TVPNodeClient::SlotContext.Timestamp );
1914 // Set the position or orientation received along with the mode in the database
1915 uint8 modeEnum = (uint8)(mode44 & 0xFF);
1916 if ( modeEnum == MBEHAV::COMBAT_FLOAT )
1918 // Set theta
1919 if (_DataBase != NULL && (!IgnoreEntityDbUpdates || slot==0))
1921 CCDBNodeBranch *nodeRoot;
1922 nodeRoot = dynamic_cast<CCDBNodeBranch*>(_DataBase->getNode(0));
1923 if ( nodeRoot )
1925 CCDBNodeLeaf *node = dynamic_cast<CCDBNodeLeaf*>(nodeRoot->getNode(slot)->getNode( PROPERTY_ORIENTATION ));
1926 nlassert(node != NULL);
1927 node->setValue64( (mode44 >> 12) /*&& 0xFFFFFFFF*/ );
1931 else
1933 // Set 2D position (the position at TVPNodeClient::SlotContext.Timestamp is not sent at the same time as the position for Mode)
1934 if ( _DataBase != NULL && (!IgnoreEntityDbUpdates || slot==0))
1936 uint16 x16 = (uint16)((ac->getValue() >> 12) & 0xFFFF);
1937 uint16 y16 = (uint16)((ac->getValue() >> 28) & 0xFFFF);
1938 if ( ! (x16==0 && y16==0) ) // don't set the position if it was not initialized yet
1940 sint32 x, y;
1941 _PropertyDecoder.decodeAbsPos2D( x, y, x16, y16 );
1943 CCDBNodeBranch *nodeRoot;
1944 nodeRoot = dynamic_cast<CCDBNodeBranch*>(_DataBase->getNode(0));
1945 if ( nodeRoot )
1947 CCDBNodeLeaf *node;
1948 node = dynamic_cast<CCDBNodeLeaf*>(nodeRoot->getNode(slot)->getNode(0));
1949 nlassert(node != NULL);
1950 node->setValue64( x );
1951 node = dynamic_cast<CCDBNodeLeaf*>(nodeRoot->getNode(slot)->getNode(1));
1952 nlassert(node != NULL);
1953 node->setValue64( y );
1956 else
1958 nldebug( "%u: S%hu: Received mode with null pos", _CurrentServerTick, (uint16)slot ); // TEMP
1962 break;
1964 /* case PROPERTY_GUILD_SYMBOL:
1965 nlinfo("GuildSymbol received...");
1967 default:
1969 // special for Bars, always take _CurrentServerTick timestamp (delta timestamp decoded is mainly for position purpose...)
1970 NLMISC::TGameCycle timeStamp= TVPNodeClient::SlotContext.Timestamp;
1971 /* YOYO: i don't know what to do with others property that really use the gamecycle and are maybe buggued:
1972 ENTITY_MOUNTED_ID,RIDER_ENTITY_ID,BEHAVIOUR,TARGET_LIST,TARGET_ID,VISUAL_FX
1973 But bars timestamp accuracy is very important (else could take DB property with falsly newer timestamp)
1975 if(propIndex== PROPERTY_BARS)
1976 timeStamp= _CurrentServerTick;
1978 // Process property
1979 CChange thechange( slot, propIndex, timeStamp );
1980 _Changes.push_back( thechange );
1981 if (_DataBase != NULL && (!IgnoreEntityDbUpdates || slot==0) )
1983 CCDBNodeBranch *nodeRoot;
1984 nodeRoot = dynamic_cast<CCDBNodeBranch*>(_DataBase->getNode(0));
1985 if ( nodeRoot )
1987 CCDBNodeLeaf *node = dynamic_cast<CCDBNodeLeaf*>(nodeRoot->getNode(slot)->getNode( propIndex ));
1988 nlassert(node != NULL);
1989 node->setValue64(ac->getValue());
1990 if ( LoggingMode )
1992 nlinfo( "CLIENT: recvd property %hu (%s) for slot %hu, date %u", (uint16)propIndex, getPropText(propIndex), (uint16)slot, TVPNodeClient::SlotContext.Timestamp );
1994 //nldebug("CLPROPNET[%p]: received property %d for entity %d: %" NL_I64 "u", this, action->PropIndex, action->CLEntityId, action->getValue());
1999 CActionFactory::getInstance()->remove( (CAction*&)ac );
2001 #ifdef SHOW_PROPERTIES_RECEIVED
2002 // stringstream ss;
2003 // ss << "Received: ";
2004 // if ( propReceived[2] != 0 )
2005 // ss << propReceived[2] << " impuls. ";
2006 // if ( propReceived[0] != 0 )
2007 // ss << propReceived[0] << " pos; ";
2008 // if ( propReceived[3] != 0 )
2009 // ss << propReceived[3] << " orient; ";
2010 // uint sum = propReceived[4] + propReceived[5] + propReceived[6] + propReceived[7] + propReceived[8] + propReceived[9];
2011 // if ( sum != 0 )
2012 // ss << sum << " discreet; ";
2013 // if ( propReceived[16] != 0 )
2014 // ss << propReceived[16] << "assoc; ";
2015 // if ( propReceived[17] != 0 )
2016 // ss << propReceived[17] << "disac; ";
2017 // ss << "TOTAL: " << propReceived[2] << " + " << propReceived[0] + propReceived[3] + sum;
2019 string str = "Received: ";
2020 if ( propReceived[2] != 0 )
2021 str += NLMISC::toString(propReceived[2]) + " impuls. ";
2022 if ( propReceived[0] != 0 )
2023 str += NLMISC::toString(propReceived[0]) + " pos; ";
2024 if ( propReceived[3] != 0 )
2025 str += NLMISC::toString(propReceived[3]) + " orient; ";
2026 uint sum = propReceived[4] + propReceived[5] + propReceived[6] + propReceived[7] + propReceived[8] + propReceived[9];
2027 if ( sum != 0 )
2028 str += NLMISC::toString(sum) + " discreet; ";
2029 if ( propReceived[16] != 0 )
2030 str += NLMISC::toString(propReceived[16]) + "assoc; ";
2031 if ( propReceived[17] != 0 )
2032 str += NLMISC::toString(propReceived[17]) + "disac; ";
2033 str += "TOTAL: " + NLMISC::toString(propReceived[2]) + " + " + NLMISC::toString(propReceived[0] + propReceived[3] + sum);
2035 nlwarning( "%s", str.c_str() );
2036 #endif
2040 void CNetworkConnection::sendNormalMessage()
2042 //nlinfo("CNET[%p]: send normal message Packet=%d Ack=%d AckBits=%08X", this, _CurrentSendNumber, _LastReceivedNumber, _AckBitMask);
2045 // Create the message to send to the server
2048 CBitMemStream message;
2050 bool systemMode = false;
2052 message.serial (_CurrentSendNumber);
2053 message.serial (systemMode);
2054 message.serial (_LastReceivedNumber);
2055 message.serial (_AckBitMask);
2057 uint numPacked = 0;
2059 // pack each block
2060 // TGameCycle lastPackedCycle = 0;
2061 list<CActionBlock>::iterator itblock;
2063 //nldebug("CNET[%p]: sending message %d", this, _CurrentSendNumber);
2065 for (itblock=_Actions.begin(); itblock!=_Actions.end(); ++itblock)
2067 CActionBlock &block = *itblock;
2069 // if block contains action that are not already stamped, don't send it now
2070 if (block.Cycle == 0)
2071 break;
2073 // Prevent to send a message too big
2074 //if (message.getPosInBit() + (*itblock).bitSize() > FrontEndInputBufferSize) // hard version
2075 // break;
2077 if (block.FirstPacket == 0)
2078 block.FirstPacket = _CurrentSendNumber;
2080 //nlassertex((*itblock).Cycle > lastPackedCycle, ("(*itblock).Cycle=%d lastPackedCycle=%d", (*itblock).Cycle, lastPackedCycle));
2082 // lastPackedCycle = block.Cycle;
2084 block.serial(message);
2085 ++numPacked;
2087 //nldebug("CNET[%p]: packed block %d, message is currently %d bits long", this, block.Cycle, message.getPosInBit());
2089 // Prevent to send a message too big
2090 //if (message.getPosInBit() + (*itblock).bitSize() > FrontEndInputBufferSize) // hard version
2091 if ( message.getPosInBit() > 480*8 ) // easy version
2092 break;
2095 //_PropertyDecoder.send (_CurrentSendNumber, _LastReceivedNumber);
2096 uint32 length = message.length();
2097 _Connection.send (message.buffer(), length);
2098 //sendUDP (&(_Connection), message.buffer(), length);
2099 statsSend(length);
2100 // remember send time
2101 _LastSendTime = CTime::getLocalTime();
2103 _PacketStamps.push_back(make_pair(_CurrentSendNumber, _UpdateTime));
2105 _CurrentSendNumber++;
2108 bool CNetworkConnection::stateConnected()
2110 // if receives System PROBE
2111 // immediate state Probe
2112 // else if receives Normal
2113 // sends Normal data
2115 #ifdef ENABLE_INCOMING_MSG_RECORDER
2116 if ( ! _ReplayIncomingMessagesOn )
2117 #endif
2119 // Prevent to increment the client time when the front-end does not respond
2120 static TTime previousTime = ryzomGetLocalTime ();
2121 TTime now = ryzomGetLocalTime ();
2122 TTime diff = now - previousTime;
2123 previousTime = now;
2124 if ( (diff > 3000) && (! _Connection.dataAvailable()) )
2126 return false;
2129 // update the current time;
2130 while (_CurrentClientTime < (TTime)(_UpdateTime - _MsPerTick - _LCT) && _CurrentClientTick < _CurrentServerTick)
2132 _CurrentClientTime += _MsPerTick;
2134 _CurrentClientTick++;
2136 _MachineTimeAtTick = _UpdateTime;
2137 _MachineTicksAtTick = _UpdateTicks;
2140 if (_CurrentClientTick >= _CurrentServerTick && !_Connection.dataAvailable())
2142 return false;
2146 #ifdef ENABLE_INCOMING_MSG_RECORDER
2147 if ( ClientCfg.Local && !_ReplayIncomingMessagesOn )
2148 return false;
2149 while ( (_ReplayIncomingMessagesOn && dataToReplayAvailable()) ||
2150 _Connection.dataAvailable() )
2151 #else
2152 while (_Connection.dataAvailable())// && _TotalMessages<5)
2153 #endif
2155 _DecodedHeader = false;
2156 CBitMemStream msgin (true);
2158 if (buildStream(msgin) && decodeHeader(msgin))
2160 if (_SystemMode)
2162 uint8 message = 0;
2163 msgin.serial(message);
2165 switch (message)
2167 case SYSTEM_PROBE_CODE:
2168 // receive probe, and goto state probe
2169 _ConnectionState = Probe;
2170 // reset client impulse & vars
2172 _ImpulseDecoder.reset();
2173 _PropertyDecoder.clear();
2174 _PacketStamps.clear();
2175 // clears sent actions
2176 while (!_Actions.empty())
2177 CActionFactory::getInstance()->remove(_Actions.front().Actions),
2178 _Actions.clear();
2179 _AckBitMask = 0;
2180 _LastReceivedNumber = 0xffffffff;
2182 //nldebug("CNET[%p]: connected->probe", this);
2183 _Changes.push_back(CChange(0, ProbeReceived));
2184 receiveSystemProbe(msgin);
2185 return true;
2186 break;
2187 case SYSTEM_SYNC_CODE:
2188 // receive stalled, decode stalled and state stalled
2189 _ConnectionState = Synchronize;
2190 //nldebug("CNET[%p]: connected->synchronize", this);
2191 receiveSystemSync(msgin);
2192 return true;
2193 break;
2194 case SYSTEM_STALLED_CODE:
2195 // receive stalled, decode stalled and state stalled
2196 _ConnectionState = Stalled;
2197 //nldebug("CNET[%p]: connected->stalled", this);
2198 receiveSystemStalled(msgin);
2199 return true;
2200 break;
2201 case SYSTEM_SERVER_DOWN_CODE:
2202 disconnect(); // will send disconnection message
2203 nlwarning( "BACK-END DOWN" );
2204 return false; // exit now from loop, don't expect a new state
2205 break;
2206 default:
2207 nlwarning("CNET[%p]: received system %d in state Connected", this, message);
2208 break;
2211 else
2213 receiveNormalMessage(msgin);
2219 return false;
2226 // Probe state
2229 void CNetworkConnection::receiveSystemProbe(CBitMemStream &msgin)
2231 _LatestProbeTime = _UpdateTime;
2232 msgin.serial(_LatestProbe);
2233 _LatestProbes.push_back(_LatestProbe);
2235 //nldebug("CNET[%p]: received PROBE %d", this, _LatestProbe);
2238 // sends system sync acknowledge
2239 void CNetworkConnection::sendSystemAckProbe()
2241 CBitMemStream message;
2243 buildSystemHeader(message);
2245 uint8 probe = SYSTEM_ACK_PROBE_CODE;
2246 uint32 numprobes = (uint32)_LatestProbes.size();
2248 message.serial(probe);
2249 message.serial(numprobes);
2251 uint i;
2252 for (i=0; i<numprobes; ++i)
2253 message.serial(_LatestProbes[i]);
2255 _LatestProbes.clear();
2257 uint32 length = message.length();
2258 _Connection.send (message.buffer(), length);
2259 //sendUDP (&(_Connection), message.buffer(), length);
2260 statsSend(length);
2262 //nlinfo("CNET[%p]: sent ACK_PROBE (%d probes)", this, numprobes);
2265 bool CNetworkConnection::stateProbe()
2267 // if receives System SYNC
2268 // immediate state SYNC
2269 // else if receives System PROBE
2270 // decode PROBE
2271 // sends System ACK_PROBE
2272 #ifdef ENABLE_INCOMING_MSG_RECORDER
2273 if ( ClientCfg.Local && !_ReplayIncomingMessagesOn )
2274 return false;
2275 while ( (_ReplayIncomingMessagesOn && dataToReplayAvailable()) ||
2276 _Connection.dataAvailable() )
2277 #else
2278 while (_Connection.dataAvailable())// && _TotalMessages<5)
2279 #endif
2281 _DecodedHeader = false;
2282 CBitMemStream msgin (true);
2283 if (buildStream(msgin) && decodeHeader(msgin))
2285 if (_SystemMode)
2287 uint8 message = 0;
2288 msgin.serial(message);
2290 switch (message)
2292 case SYSTEM_SYNC_CODE:
2293 // receive sync, decode sync and state synchronize
2294 _ConnectionState = Synchronize;
2295 //nldebug("CNET[%p]: probe->synchronize", this);
2296 receiveSystemSync(msgin);
2297 return true;
2298 break;
2299 case SYSTEM_STALLED_CODE:
2300 // receive sync, decode sync and state synchronize
2301 _ConnectionState = Stalled;
2302 //nldebug("CNET[%p]: probe->stalled", this);
2303 receiveSystemStalled(msgin);
2304 return true;
2305 break;
2306 case SYSTEM_PROBE_CODE:
2307 // receive sync, decode sync
2308 receiveSystemProbe(msgin);
2309 break;
2310 case SYSTEM_SERVER_DOWN_CODE:
2311 disconnect(); // will send disconnection message
2312 nlwarning( "BACK-END DOWN" );
2313 return false; // exit now from loop, don't expect a new state
2314 break;
2315 default:
2316 nlwarning("CNET[%p]: received system %d in state Probe", message);
2317 break;
2320 else
2322 nlwarning("CNET[%p]: received normal in state Probe", this);
2323 _LatestProbeTime = _UpdateTime;
2328 // send ack sync if received sync or last sync timed out
2329 if (!_LatestProbes.empty() || _UpdateTime-_LatestProbeTime > 300)
2331 sendSystemAckProbe();
2332 _LatestProbeTime = _UpdateTime;
2334 else
2335 nlSleep(10);
2337 return false;
2345 // Stalled state
2348 void CNetworkConnection::receiveSystemStalled(CBitMemStream &/* msgin */)
2350 nldebug("CNET[%p]: received STALLED", this);
2353 bool CNetworkConnection::stateStalled()
2355 // if receives System SYNC
2356 // immediate state SYNC
2357 // else if receives System STALLED
2358 // decode STALLED (nothing to do)
2359 // else if receives System PROBE
2360 // immediate state PROBE
2361 #ifdef ENABLE_INCOMING_MSG_RECORDER
2362 if ( ClientCfg.Local && !_ReplayIncomingMessagesOn )
2363 return false;
2364 while ( (_ReplayIncomingMessagesOn && dataToReplayAvailable()) ||
2365 _Connection.dataAvailable() )
2366 #else
2367 while (_Connection.dataAvailable())// && _TotalMessages<5)
2368 #endif
2370 _DecodedHeader = false;
2371 CBitMemStream msgin (true);
2372 if (buildStream(msgin) && decodeHeader(msgin))
2374 if (_SystemMode)
2376 uint8 message = 0;
2377 msgin.serial(message);
2379 switch (message)
2381 case SYSTEM_SYNC_CODE:
2382 // receive sync, decode sync and state synchronize
2383 _ConnectionState = Synchronize;
2384 nldebug("CNET[%p]: stalled->synchronize", this);
2385 receiveSystemSync(msgin);
2386 return true;
2387 break;
2388 case SYSTEM_PROBE_CODE:
2389 // receive sync, decode sync
2390 _ConnectionState = Probe;
2391 nldebug("CNET[%p]: stalled->probe", this);
2392 receiveSystemProbe(msgin);
2393 break;
2394 case SYSTEM_STALLED_CODE:
2395 // receive stalled, decode stalled
2396 receiveSystemStalled(msgin);
2397 break;
2398 case SYSTEM_SERVER_DOWN_CODE:
2399 disconnect(); // will send disconnection message
2400 nlwarning( "BACK-END DOWN" );
2401 return false; // exit now from loop, don't expect a new state
2402 break;
2403 default:
2404 nlwarning("CNET[%p]: received system %d in state Stalled", message);
2405 break;
2408 else
2410 nlwarning("CNET[%p]: received normal in state Stalled", this);
2415 return false;
2423 // encoding / decoding methods
2426 bool CNetworkConnection::decodeHeader(CBitMemStream &msgin, bool checkMessageNumber)
2428 if (_DecodedHeader)
2429 return true;
2431 // simulate packet loss
2432 #if !FINAL_VERSION
2433 if(uint((rand()%100)) < ClientCfg.SimulatePacketLossRatio)
2434 return false;
2435 #endif
2437 ++_TotalMessages;
2439 _LastReceivedTime = _UpdateTime;
2441 msgin.serial (_CurrentReceivedNumber);
2442 msgin.serial (_SystemMode);
2444 if ((sint)_CurrentReceivedNumber > (sint)_LastReceivedPacketInBothModes && checkMessageNumber)
2446 _TotalLostPackets += (_CurrentReceivedNumber-_LastReceivedPacketInBothModes - 1);
2447 _LastReceivedPacketInBothModes = _CurrentReceivedNumber;
2450 _MeanPackets.update((float)_CurrentReceivedNumber, ryzomGetLocalTime ());
2452 /// \todo remove
2453 //nlinfo("DBG:BEN: decodeHeader, packet=%d, %s", _CurrentReceivedNumber, _SystemMode ? "SYSTEM" : "NORMAL");
2456 if (_SystemMode)
2459 else
2461 msgin.serial (_LastReceivedAck);
2463 #ifdef INCLUDE_FE_STATS_IN_PACKETS
2464 // receive debug info in the message header
2465 uint32 UserLWatch,
2466 CycleWatch,
2467 ReceiveWatch,
2468 SendWatch;
2469 float PriorityAmount;
2470 uint16 SeenEntities;
2471 //float HpT;
2472 msgin.serial (UserLWatch);
2473 msgin.serial (CycleWatch);
2474 msgin.serial (ReceiveWatch);
2475 msgin.serial (SendWatch);
2476 msgin.serial (PriorityAmount);
2477 msgin.serial (SeenEntities);
2478 //msgin.serial (HpT);
2479 #endif
2481 #ifdef DISPLAY_ENTITIES
2482 UserLWatchGraph.addOneValue ((float)UserLWatch);
2483 CycleWatchGraph.addOneValue ((float)CycleWatch);
2484 ReceiveWatchGraph.addOneValue ((float)ReceiveWatch);
2485 SendWatchGraph.addOneValue ((float)SendWatch);
2486 PriorityAmountGraph.addOneValue(PriorityAmount);
2487 SeenEntitiesGraph.addOneValue(SeenEntities);
2488 //HPThreshold = HpT;
2489 #else
2490 static sint counter = 128;
2491 --counter;
2492 if ( counter == 0 )
2494 // nlinfo( "User:%u Cycle:%u Rcv:%u Snd:%u PrioAmount:%.2f",
2495 // UserLWatch, CycleWatch, ReceiveWatch, SendWatch, PriorityAmount );
2496 counter = 128;
2498 #endif // DISPLAY_ENTITIES
2501 if (!checkMessageNumber)
2502 return true;
2504 // display info on this packet
2505 //nlinfo("CNET[%p] received packet %d, %s mode - LastReceivedAck=%d", this, _CurrentReceivedNumber, _SystemMode ? "SYTEM" : "NORMAL", _LastReceivedAck);
2507 // todo doesn't work if we receive the packet in bad order or 2 same packet
2509 if (_CurrentReceivedNumber > _LastReceivedNumber+1)
2511 // we lost some messages...
2512 nldebug ("CNET[%p] lost messages server->client [%d; %d]", this, _LastReceivedNumber+1, _CurrentReceivedNumber-1);
2513 _MeanLoss.update((float)(_CurrentReceivedNumber-_LastReceivedNumber-1), ryzomGetLocalTime ());
2515 else if (_CurrentReceivedNumber == _LastReceivedNumber)
2517 // we receive the same packet that the last one
2518 nldebug ("CNET[%p] awaiting packet %d, received packet %d", this, _LastReceivedNumber+1, _CurrentReceivedNumber);
2519 return false;
2521 else if (_CurrentReceivedNumber < _LastReceivedNumber)
2523 // it's an older message than the current
2524 nldebug ("CNET[%p] received an old message, awaiting packet %d, received packet %d", this, _LastReceivedNumber+1, _CurrentReceivedNumber);
2525 return false;
2528 // don't acknowledge system messages and normal messages in
2529 // because this will disturb impulsion from frontend, that will interpret it as if previous messages were ok
2530 bool ackBool = (!_SystemMode && (_ConnectionState == Connected || _ConnectionState == Synchronize));
2531 uint ackBit = (ackBool ? 1 : 0);
2533 if (_CurrentReceivedNumber - _LastReceivedNumber < 32)
2535 _AckBitMask <<= _CurrentReceivedNumber - _LastReceivedNumber;
2536 _AckBitMask |= _LastAckBit << (_CurrentReceivedNumber - _LastReceivedNumber - 1);
2538 else
2540 _AckBitMask = (_CurrentReceivedNumber - _LastReceivedNumber == 32 && _LastAckBit != 0) ? 0x80000000 : 0x00000000;
2543 _LastAckBit = ackBit;
2545 // encode long ack bitfield
2546 TPacketNumber i;
2547 for (i=_LastReceivedNumber+1; i<_CurrentReceivedNumber; ++i)
2548 _LongAckBitField.clear(i&(NumBitsInLongAck-1));
2550 _LongAckBitField.set(_CurrentReceivedNumber&(NumBitsInLongAck-1), ackBool);
2553 // no more than NumBitsInLongAck ack in field
2554 if ((TPacketNumber)_LastAckInLongAck <= (TPacketNumber)(_CurrentReceivedNumber-NumBitsInLongAck))
2555 _LastAckInLongAck = _CurrentReceivedNumber-NumBitsInLongAck+1;
2557 _LastReceivedNumber = _CurrentReceivedNumber;
2559 _DecodedHeader = true;
2561 return true;
2564 void CNetworkConnection::buildSystemHeader(NLMISC::CBitMemStream &msgout)
2566 msgout.serial (_CurrentSendNumber);
2567 bool systemMode = true;
2568 msgout.serial (systemMode);
2570 _PacketStamps.push_back(make_pair(_CurrentSendNumber, _UpdateTime));
2572 ++_CurrentSendNumber;
2580 void CNetworkConnection::setDataBase(CCDBNodeBranch *database)
2582 _DataBase = database;
2590 void CNetworkConnection::push(CBitMemStream &msg)
2592 sint32 maxImpulseBitSize = 230*8;
2594 CActionGeneric *ag = (CActionGeneric *)CActionFactory::getInstance ()->create (INVALID_SLOT, ACTION_GENERIC_CODE);
2595 if( ag == NULL ) //TODO: see that with oliver...
2596 return;
2598 uint bytelen = msg.length();
2599 sint32 impulseMinBitSize = (sint32)CActionFactory::getInstance ()->size( ag );
2600 sint32 impulseBitSize = impulseMinBitSize + (4 + bytelen)*8;
2602 if (impulseBitSize < maxImpulseBitSize)
2604 ag->set(msg);
2605 push(ag);
2607 else
2609 CAction *casted = ag;
2610 CActionFactory::getInstance()->remove(casted);
2611 ag = NULL;
2613 // MultiPart impulsion
2614 CActionGenericMultiPart *agmp = (CActionGenericMultiPart *)CActionFactory::getInstance ()->create (INVALID_SLOT, ACTION_GENERIC_MULTI_PART_CODE);
2615 sint32 minimumBitSizeForMP = CActionFactory::getInstance ()->size (agmp);
2617 sint32 availableSize = (maxImpulseBitSize - minimumBitSizeForMP) / 8; // (in bytes)
2618 #ifdef NL_DEBUG
2619 nlassert( availableSize > 0 ); // the available size must be larger than the 'empty' size
2620 #endif
2621 sint32 nbBlock = (bytelen + availableSize - 1) / availableSize;
2623 uint8 num = _ImpulseMultiPartNumber++;
2625 for (sint32 i = 0; i < nbBlock; i++)
2627 if (i != 0)
2628 agmp = (CActionGenericMultiPart *)CActionFactory::getInstance ()->create (INVALID_SLOT, ACTION_GENERIC_MULTI_PART_CODE);
2630 agmp->set(num, (uint16)i, msg.buffer(), bytelen, availableSize, (uint16)nbBlock);
2631 push(agmp);
2636 void CNetworkConnection::pushTarget(TCLEntityId slot, LHSTATE::TLHState targetOrPickup )
2638 CActionTargetSlot *ats = (CActionTargetSlot*)CActionFactory::getInstance ()->create (INVALID_SLOT, ACTION_TARGET_SLOT_CODE);
2639 nlassert (ats != NULL);
2640 ats->Slot = slot;
2641 switch ( targetOrPickup ) // ensure the value is good for the FE
2643 case LHSTATE::NONE: ats->TargetOrPickup = 0; break;
2644 case LHSTATE::LOOTABLE: ats->TargetOrPickup = 1; break;
2645 case LHSTATE::HARVESTABLE: ats->TargetOrPickup = 2; break;
2646 default:
2647 break;
2650 ats->TargetOrPickup = (uint32)targetOrPickup;
2651 push(ats);
2655 void CNetworkConnection::push(CAction *action)
2657 if (_Actions.empty() || _Actions.back().Cycle != 0)
2659 //nlinfo("-BEEN- push back 2 [size=%d, cycle=%d]", _Actions.size(), _Actions.empty() ? 0 : _Actions.back().Cycle);
2660 _Actions.push_back(CLFECOMMON::CActionBlock());
2663 _Actions.back().Actions.push_back(action);
2669 void CNetworkConnection::send(TGameCycle cycle)
2673 // check the current game cycle was not already sent
2674 nlassertex(_LastSentCycle < cycle, ("Client=%p, _LastSentCycle=%d, cycle=%d", this, _LastSentCycle, cycle));
2677 if (_LastSentCycle == cycle) // delay send till next tick
2678 return;
2680 _LastSentCycle = cycle;
2682 // if no actions were sent at this cyle, create a new block
2683 if (_Actions.empty() || _Actions.back().Cycle != 0)
2685 // nlinfo("-BEEN- push back 1 [size=%d, cycle=%d]", _Actions.size(), _Actions.empty() ? 0 : _Actions.back().Cycle);
2686 // _Actions.push_back();
2688 else
2690 CActionBlock &block = _Actions.back();
2692 CAction *dummy = CActionFactory::getInstance()->create(INVALID_SLOT, ACTION_DUMMY_CODE);
2693 ((CActionDummy*)dummy)->Dummy1 = _DummySend++;
2694 push(dummy);
2697 _Actions.back().Cycle = cycle;
2699 // check last block isn't bigger than maximum allowed
2700 uint i;
2701 uint bitSize = 32+8; // block size is 32 (cycle) + 8 (number of actions
2702 for (i=0; i<block.Actions.size(); ++i)
2704 bitSize += CActionFactory::getInstance()->size(block.Actions[i]);
2705 if (bitSize >= 480*8)
2706 break;
2709 if (i<block.Actions.size())
2711 nldebug( "Postponing %u actions exceeding max size in block %d (block size is %d bits long)", block.Actions.size()-i, cycle, bitSize );
2713 // last block is bigger than allowed
2715 // allocate a new block
2716 _Actions.push_back(CActionBlock());
2717 CActionBlock &newBlock = _Actions.back();
2719 // reset block stamp
2720 newBlock.Cycle = 0;
2722 // copy remaining actions in new block
2723 newBlock.Actions.insert(newBlock.Actions.begin(),
2724 block.Actions.begin()+i,
2725 block.Actions.end());
2727 // remove remaining actions of block
2728 block.Actions.erase(block.Actions.begin()+i, block.Actions.end());
2731 //nlinfo("-BEEN- setcycle [size=%d, cycle=%d]", _Actions.size(), _Actions.empty() ? 0 : _Actions.back().Cycle);
2734 if (_ConnectionState == Connected)
2736 sendNormalMessage();
2739 catch (const ESocket &/*e*/)
2741 _ConnectionState = Disconnect;
2742 disconnect(); // won't send disconnection message as state is already Disconnect
2747 void CNetworkConnection::send()
2751 // Send is temporised, that is the packet may not be actually sent.
2752 // We don't care, since:
2753 // - this packet has no new data (not ticked send)
2754 // - a next send() will send packet if time elapsed enough
2755 // - a next send(tick) will really be sent
2756 // This way, we can say that at most 15 packets will be delivered each second
2757 // (5 send(tick), and 10 send() -- if you take getLocalTime() inaccuracy into account)
2758 if (_ConnectionState == Connected && CTime::getLocalTime()-_LastSendTime > 100)
2760 sendNormalMessage();
2763 catch (const ESocket &/*e*/)
2765 _ConnectionState = Disconnect;
2773 // sends system sync acknowledge
2774 void CNetworkConnection::sendSystemDisconnection()
2776 CBitMemStream message;
2778 buildSystemHeader(message);
2780 uint8 disconnection = SYSTEM_DISCONNECTION_CODE;
2782 message.serial(disconnection);
2784 uint32 length = message.length();
2786 if (_Connection.connected())
2790 _Connection.send(message.buffer(), length);
2792 catch (const ESocket &e)
2794 nlwarning("Socket exception: %s", e.what());
2797 //sendUDP (&(_Connection), message.buffer(), length);
2798 statsSend(length);
2800 updateBufferizedPackets ();
2801 nlinfo("CNET[%p]: sent DISCONNECTION", this);
2804 void CNetworkConnection::disconnect()
2806 #ifdef MEASURE_RECEIVE_DATES
2807 if ( LogReceiveEnabled )
2809 displayReceiveLog();
2811 #endif
2813 if (_ConnectionState == NotInitialised ||
2814 _ConnectionState == NotConnected ||
2815 _ConnectionState == Authenticate ||
2816 _ConnectionState == Disconnect)
2818 //nlwarning("Unable to disconnect(): not connected yet, or already disconnected.");
2819 return;
2822 sendSystemDisconnection();
2823 _Connection.close();
2824 _ConnectionState = Disconnect;
2830 // Quit state
2833 void CNetworkConnection::receiveSystemAckQuit(CBitMemStream &/* msgin */)
2835 nldebug("CNET[%p]: received ACK_QUIT", this);
2836 _ReceivedAckQuit = true;
2840 bool CNetworkConnection::stateQuit()
2843 #ifdef ENABLE_INCOMING_MSG_RECORDER
2844 if ( ClientCfg.Local && !_ReplayIncomingMessagesOn )
2845 return false;
2846 while ( (_ReplayIncomingMessagesOn && dataToReplayAvailable()) ||
2847 _Connection.dataAvailable() )
2848 #else
2849 while (_Connection.dataAvailable())// && _TotalMessages<5)
2850 #endif
2852 _DecodedHeader = false;
2853 CBitMemStream msgin (true);
2854 if (buildStream(msgin) && decodeHeader(msgin, false))
2856 if (_SystemMode)
2858 uint8 message = 0;
2859 msgin.serial(message);
2861 switch (message)
2863 case SYSTEM_SYNC_CODE:
2864 // receive sync, decode sync and state synchronize
2865 reset();
2866 _ConnectionState = Synchronize;
2867 nldebug("CNET[%p]: quit->synchronize", this);
2868 receiveSystemSync(msgin);
2869 return true;
2870 break;
2872 case SYSTEM_PROBE_CODE:
2873 // receive sync, decode sync
2874 _ConnectionState = Probe;
2875 nldebug("CNET[%p]: stalled->probe", this);
2876 receiveSystemProbe(msgin);
2877 break;
2878 case SYSTEM_STALLED_CODE:
2879 // receive stalled, decode stalled
2880 _ConnectionState = Stalled;
2881 receiveSystemStalled(msgin);
2882 return true;
2883 break;
2885 case SYSTEM_SERVER_DOWN_CODE:
2886 disconnect(); // will send disconnection message
2887 nlwarning( "BACK-END DOWN" );
2888 return false; // exit now from loop, don't expect a new state
2889 break;
2890 case SYSTEM_ACK_QUIT_CODE:
2891 // receive ack quit -> reset connection state
2892 receiveSystemAckQuit(msgin);
2893 break;
2894 default:
2895 nlwarning("CNET[%p]: received system %d in state Stalled", message);
2896 break;
2899 else
2901 nlwarning("CNET[%p]: received normal in state Stalled", this);
2906 // send quit if not yet received a ack quit
2907 if (!_ReceivedAckQuit && _UpdateTime-_LatestQuitTime > 100)
2909 sendSystemQuit();
2910 _LatestQuitTime = _UpdateTime;
2913 return false;
2917 * Quit the game till the connection is reset
2919 void CNetworkConnection::quit()
2921 nlinfo("CNetworkConnection::quit() called, setting to quitting state.");
2923 ++_QuitId;
2924 _ConnectionState = Quit;
2925 _ReceivedAckQuit = false;
2926 _LatestQuitTime = _UpdateTime;
2928 sendSystemQuit();
2931 void CNetworkConnection::reset()
2933 _CurrentSendNumber = 0;
2934 _LastReceivedNumber = 0;
2935 _LastReceivedTime = 0;
2936 _LastReceivedNormalTime = 0;
2937 _AckBitMask = 0;
2938 _LastAckBit = 0;
2940 _Synchronize = 0;
2941 _InstantPing = 10000;
2942 _BestPing = 10000;
2943 _LCT = 100;
2944 _MachineTimeAtTick = ryzomGetLocalTime ();
2945 _MachineTicksAtTick = CTime::getPerformanceTime();
2946 _LastSentSync = 0;
2947 _LatestSync = 0;
2949 _PropertyDecoder.init (256);
2951 _DummySend = 0;
2952 _LongAckBitField.resize(1024);
2953 _LastAckInLongAck = 0;
2954 _LastSentCycle = 0;
2956 _TotalReceivedBytes = 0;
2957 _PartialReceivedBytes = 0;
2958 _TotalSentBytes = 0;
2959 _PartialSentBytes = 0;
2960 _MeanPackets.MeanPeriod = 5000;
2961 _MeanLoss.MeanPeriod = 5000;
2963 _LastReceivedPacketInBothModes = 0;
2964 _TotalLostPackets = 0;
2965 _ConnectionQuality = false;
2967 _CurrentSmoothServerTick= 0;
2968 _SSTLastLocalTime= 0;
2971 void CNetworkConnection::initTicks()
2973 _CurrentClientTick = 0;
2974 _CurrentServerTick = 0;
2975 _MsPerTick = 100;
2976 _LCT = LCT;
2979 void CNetworkConnection::reinit()
2981 // Reset data
2982 _ImpulseDecoder.reset();
2983 if (_DataBase)
2984 _DataBase->resetData(_CurrentServerTick, true);
2985 _LongAckBitField.clearAll();
2986 _PacketStamps.clear();
2987 _Actions.clear();
2988 _Changes.clear();
2989 _GenericMultiPartTemp.clear();
2990 _IdMap.clear();
2991 reset();
2992 initTicks();
2994 // Reuse the udp socket
2995 _Connection.~CUdpSimSock();
2997 #ifdef new
2998 #undef new
2999 #endif
3000 new (&_Connection) CUdpSimSock();
3002 #ifdef DEBUG_NEW
3003 #define new DEBUG_NEW
3004 #endif
3007 // sends system sync acknowledge
3008 void CNetworkConnection::sendSystemQuit()
3010 CBitMemStream message;
3012 buildSystemHeader(message);
3014 uint8 quit = SYSTEM_QUIT_CODE;
3016 message.serial(quit);
3017 message.serial(_QuitId);
3019 uint32 length = message.length();
3020 _Connection.send (message.buffer(), length);
3021 //sendUDP (&(_Connection), message.buffer(), length);
3022 statsSend(length);
3024 updateBufferizedPackets ();
3025 nlinfo("CNET[%p]: sent QUIT", this);
3030 void CNetworkConnection::displayAllocationStats()
3032 nlinfo("CNET[%p]: %d queued blocks, %d changes", this, _Actions.size(), _Changes.size());
3035 string CNetworkConnection::getAllocationStats()
3037 char buf[128];
3038 sprintf(buf, "CNET[%p]: %u queued blocks, %u changes", this, (uint)_Actions.size(), (uint)_Changes.size());
3039 return string(buf);
3047 void CNetworkConnection::genericAction (CActionGeneric *ag)
3049 // manage a generic action
3050 CBitMemStream &bms = ag->get ();
3052 //nldebug("CNET: Calling impulsion callback (size %u) :'%s'", this, bms.length(), toHexaString(bms.bufferAsVector()).c_str());
3053 //nldebug("CNET[%p]: Calling impulsion callback (size %u)", this, bms.length());
3055 if (_ImpulseCallback != NULL)
3056 _ImpulseCallback(bms, _LastReceivedNumber, _ImpulseArg);
3059 void CNetworkConnection::CGenericMultiPartTemp::set (CActionGenericMultiPart *agmp, CNetworkConnection *parent)
3061 if (NbBlock == 0xFFFFFFFF)
3063 // new GenericMultiPart
3064 NbBlock = agmp->NbBlock;
3065 NbCurrentBlock = 0;
3066 TempSize = 0;
3067 Temp.clear();
3068 Temp.resize(NbBlock);
3069 BlockReceived.resize(NbBlock);
3070 for (uint i = 0; i < NbBlock; i++)
3071 BlockReceived[i] = false;
3074 nlassert (NbBlock == agmp->NbBlock);
3075 nlassert (NbBlock > agmp->Part);
3077 // check if the block was already received
3078 if (BlockReceived[agmp->Part])
3080 nlwarning ("CLMPNET[%p]: This part is already received, discard it", this);
3081 return;
3084 Temp[agmp->Part] = agmp->PartCont;
3085 BlockReceived[agmp->Part] = true;
3087 NbCurrentBlock++;
3088 TempSize += (uint32)agmp->PartCont.size();
3090 if (NbCurrentBlock == NbBlock)
3092 // reform the total action
3094 //nldebug("CLMPNET[%p]: Received a TOTAL generic action MP size: number %d nbblock %d", this, agmp->Number, NbBlock);
3096 CBitMemStream bms(true);
3098 uint8 *ptr = bms.bufferToFill (TempSize);
3100 for (uint i = 0; i < Temp.size (); i++)
3102 memcpy (ptr, &(Temp[i][0]), Temp[i].size());
3103 ptr += Temp[i].size();
3106 NbBlock = 0xFFFFFFFF;
3108 //nldebug("CLMPNET[%p]: Received a generic action size %d", this, bms.length());
3109 // todo interface api, call a user callback
3111 if (parent->_ImpulseCallback != NULL)
3112 parent->_ImpulseCallback(bms, parent->_LastReceivedNumber, parent->_ImpulseArg);
3117 void CNetworkConnection::genericAction (CActionGenericMultiPart *agmp)
3119 // manage a generic action (big one that comes by blocks)
3120 vector<uint8> &v = agmp->PartCont;
3122 //nldebug("CLMPNET[%p]: Received a generic action MP size %d: number %d part %d nbblock %d", this, v.size(), agmp->Number, agmp->Part, agmp->NbBlock);
3124 // add it
3126 if (_GenericMultiPartTemp.size() <= agmp->Number)
3128 _GenericMultiPartTemp.resize (agmp->Number+1);
3131 _GenericMultiPartTemp[agmp->Number].set(agmp, this);
3136 CNetworkConnection::TVPNodeClient::TSlotContext CNetworkConnection::TVPNodeClient::SlotContext;
3139 * Return the average billed upload rate in kbps, including all headers (UDP+IP+Ethernet)
3141 void CNetworkConnection::statsSend(uint32 bytes)
3143 _TotalSentBytes += bytes;
3144 _PartialSentBytes += bytes;
3145 _MeanUpload.update((float)bytes, ryzomGetLocalTime ());
3147 UploadGraph.addValue ((float)bytes);
3152 * Return the average billed download rate in kbps, including all headers (UDP+IP+Ethernet)
3154 void CNetworkConnection::statsReceive(uint32 bytes)
3156 _TotalReceivedBytes += bytes;
3157 _PartialReceivedBytes += bytes;
3158 _MeanDownload.update((float)bytes, ryzomGetLocalTime ());
3160 DownloadGraph.addValue ((float)bytes);
3164 NLMISC_COMMAND( displayPosUpdateGraph, "Display position update interval graph", "0|<slot>" )
3166 // Stop graph in all cases
3167 if ( PosUpdateIntervalGraph )
3169 delete PosUpdateIntervalGraph;
3170 delete PosUpdatePredictionGraph;
3171 PosUpdateIntervalGraph = NULL;
3172 PosUpdatePredictionGraph = NULL;
3175 // Start graph if argument is not 0
3176 if ( (args.size() != 0) && (args[0] != "0") )
3178 uint8 slot;
3179 fromString(args[0], slot);
3180 PosUpdateIntervalGraph = new CSlotGraph( "Interval", 350, 2, 100, 200, CRGBA(128,0,0,64), 1000, 20, true, slot );
3181 PosUpdatePredictionGraph = new CSlotGraph( " Prediction", 350, 2, 100, 200, CRGBA(0,0,128,64), 1000, 20, true, slot );
3183 return true;
3187 // ***************************************************************************
3188 sint64 CNetworkConnection::convertToSmoothServerTick(NLMISC::TGameCycle t) const
3190 return t*SMOOTH_SERVER_TICK_PER_TICK;
3194 // ***************************************************************************
3195 void CNetworkConnection::updateSmoothServerTick()
3197 // Get deltaT
3198 NLMISC::TTime t= ryzomGetLocalTime ();
3199 sint32 deltaT= (sint32)(t - _SSTLastLocalTime);
3200 _SSTLastLocalTime= t;
3202 // Get the actual ServerTick not smoothed value
3203 sint64 actualST= _CurrentServerTick*SMOOTH_SERVER_TICK_PER_TICK;
3205 // Special bound cases
3206 if( _CurrentSmoothServerTick < actualST+SMOOTH_SERVER_TICK_DIFF_MIN )
3208 // Reset (possible jump to future!)
3209 _CurrentSmoothServerTick= actualST;
3211 else if( _CurrentSmoothServerTick>= actualST+SMOOTH_SERVER_TICK_DIFF_MAX )
3213 // Clamp (no reset, no back to past!)
3214 _CurrentSmoothServerTick= actualST+SMOOTH_SERVER_TICK_DIFF_MAX;
3216 else
3218 // Compute the Factor of acceleration according to error difference (FIXED 16:16)
3219 sint64 factor;
3220 sint64 errorDiff= _CurrentSmoothServerTick-actualST;
3221 // If the estimation is in the TimeWindow, no acceleration
3222 if( errorDiff>=-SMOOTH_SERVER_TICK_WINDOW && errorDiff<=SMOOTH_SERVER_TICK_WINDOW)
3224 factor= 65536;
3226 // If the estimation is late, accelerate
3227 else if( errorDiff<0 )
3229 float f= (float)(errorDiff+SMOOTH_SERVER_TICK_WINDOW)/(SMOOTH_SERVER_TICK_DIFF_MIN+SMOOTH_SERVER_TICK_WINDOW);
3230 f*= SMOOTH_SERVER_TICK_ACCEL;
3231 factor= sint64(f*65536);
3233 // Else the estimation is too early, slowDown
3234 else
3236 float f= (float)(errorDiff-SMOOTH_SERVER_TICK_WINDOW)/(SMOOTH_SERVER_TICK_DIFF_MAX-SMOOTH_SERVER_TICK_WINDOW);
3237 f= 1-f;
3238 factor= sint64(f*65536);
3241 // Update the Smooth
3242 _CurrentSmoothServerTick+= ((deltaT*SMOOTH_SERVER_TICK_PER_TICK*factor)/getMsPerTick()) >> 16;