1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2013 Laszlo KIS-ADAM (dfighter) <dfighter1985@gmail.com>
6 // Copyright (C) 2014-2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
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/>.
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
66 #ifdef DISPLAY_ENTITIES
67 #include "../../../test/network/sb5000/client/graph.h"
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 // ***************************************************************************
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>
107 #define new DEBUG_NEW
110 // Stat: array of vectors of cycles when a pos is received, indexed by TCLEntityId
113 TRDateState( TGameCycle gc
, TGameCycle pdit
, TTime ct
) : ServerCycle(gc
), PredictedInterval(pdit
), LocalTime(ct
) {}
115 TGameCycle ServerCycle
, PredictedInterval
;
120 extern CLFECOMMON::TCLEntityId WatchedEntitySlot
;
127 typedef vector
<TRDateState
> TReceiveDateLog
;
128 TReceiveDateLog ReceivePosDateLog
[256];
129 bool LogReceiveEnabled
= false;
130 CConfigFile NCConfigFile
;
131 CFileDisplayer
*ReceiveLogDisp
;
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();
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()
163 for ( i
=0; i
!=256; ++i
)
165 ReceivePosDateLog
[i
].clear();
168 LogReceiveEnabled
= true;
174 void stopReceiveLog()
176 LogReceiveEnabled
= false;
182 void displayReceiveLog()
184 if ( ! LogReceiveEnabled
)
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)
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" );
215 NLMISC_COMMAND( stopReceiveLog
, "Stops logging the position receives", "" )
221 NLMISC_COMMAND( displayReceiveLog
, "Flush the receive log into ReceiveLog.log", "" )
230 #define new DEBUG_NEW
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);
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();
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();
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;
310 #ifdef ENABLE_INCOMING_MSG_RECORDER
311 CNetworkConnection::CNetworkConnection() : _NextMessageToReplay(true)
313 CNetworkConnection::CNetworkConnection()
316 _ConnectionState
= NotInitialised
;
317 _ImpulseCallback
= NULL
;
332 CNetworkConnection::~CNetworkConnection()
334 #ifdef MEASURE_RECEIVE_DATES
335 delete ReceiveLogDisp
;
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();
352 NLMISC::CHashKeyMD5
getTextMD5(const std::string
& filename
)
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')
367 if (it
!= buffer
.end())
368 it
= buffer
.erase(it
);
370 while (it
!= buffer
.end());
372 return NLMISC::getMD5((&buffer
[0]), (uint32
)buffer
.size());
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.");
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
);
402 initCookie(cookie
, addr
);
404 #ifdef HALF_FREQUENCY_SENDING_TO_CLIENT
405 nlinfo( "Half-frequency mode" );
407 nlinfo( "Full-frequency mode" );
410 #ifdef MEASURE_RECEIVE_DATES
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;
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
)
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
;
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
);
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;
501 nlwarning( "Cannot open %s for recording", filename
.c_str() );
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
);
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() )
535 nlinfo( "Nothing to replay" );
536 _RecordedMessagesIn
.close();
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;
551 nlwarning( "File %s for replay not found", filename
.c_str() );
557 if ( _RecordIncomingMessagesOn
)
558 _RecordedMessagesIn
.close();
559 _ReplayIncomingMessagesOn
= false;
565 bool CNetworkConnection::connect(string
&result
)
567 if (_ConnectionState
!= NotConnected
)
569 nlwarning("Unable to connect(): connection not properly initialised (maybe connection not closed).");
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
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
);
587 nlinfo ("Try to find a shard that have fsaddr = '%s'", fsaddr
.asString().c_str());
589 CConfigFile::CVar
&shards
= cfg
.getVar("Shards");
592 for (i
= 0; i
< shards
.size(); i
+=2)
596 net
.setNameAndPort (shards
.asString(i
));
597 nlinfo ("testAddr = '%s'", net
.asString().c_str());
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");
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);
626 nlwarning ("the copy command seems failed with the error code %d, errno %d: %s", ret
, errno
, strerror(errno
));
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());
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());
678 _ConnectionState
= Login
;
680 _LatestLoginTime
= ryzomGetLocalTime ();
681 _LatestSyncTime
= _LatestLoginTime
;
682 _LatestProbeTime
= _LatestLoginTime
;
685 nlinfo("CNET[%p]: Client connected to shard, attempting login", this);
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()...
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 );
740 _UpdateTime
= ryzomGetLocalTime ();
741 _UpdateTicks
= ryzomGetPerformanceTime();
742 _ReceivedSync
= false;
743 _NormalPacketsReceived
= 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
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 );
765 _ConnectionState
= Connected
;
769 CBitMemStream
msgin( true );
770 bool res
= buildStream( msgin
);
774 static sint32 loopcount
= 0;
776 static TTicks lastdisplay
= CTime::getPerformanceTime();
777 TTicks tn
= CTime::getPerformanceTime();
778 TTime diff
= CTime::ticksToSecond(tn
- lastdisplay
) * 1000.0;
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
);
790 if (!_Connection
.connected())
792 //if(!ClientCfg.Local)
793 // nlwarning("CNET[%p]: update() attempted whereas socket is not connected !", this);
800 bool stateBroke
= false;
803 switch (_ConnectionState
)
806 // if receives System SYNC
807 // immediate state Synchronize
809 // sends System LoginCookie
810 stateBroke
= stateLogin();
814 // if receives System PROBE
815 // immediate state Probe
816 // else if receives Normal
817 // immediate state Connected
819 // sends System ACK_SYNC
820 stateBroke
= stateSynchronize();
824 // if receives System PROBE
825 // immediate state Probe
826 // else if receives Normal
828 stateBroke
= stateConnected();
832 // if receives System SYNC
833 // immediate state SYNC
834 // else if receives System PROBE
836 // sends System ACK_PROBE
837 stateBroke
= stateProbe();
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();
851 // if receives System SYNC
852 // immediate state Synchronize
854 // sends System LoginCookie
855 stateBroke
= stateQuit();
860 stateBroke
= false; // will come here if a disconnection action is received inside a method that returns true
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
)
891 statsReceive( _NextMessageToReplay
.length() );
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" );
905 _RecordedMessagesIn
.serial( _NextClientTickToReplay
);
906 _NextMessageToReplay
.clear();
907 _RecordedMessagesIn
.serialMemStream( _NextMessageToReplay
);
915 if ( _Connection
.receive( (uint8
*)_ReceiveBuffer
, len
, false ) )
917 // Compute some statistics
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?
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" );
947 // Client automaton states methods
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
);
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
)
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() );
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
1027 // sends System LoginCookie
1029 #ifdef ENABLE_INCOMING_MSG_RECORDER
1030 if ( ClientCfg
.Local
&& !_ReplayIncomingMessagesOn
)
1032 while ( (_ReplayIncomingMessagesOn
&& dataToReplayAvailable()) ||
1033 _Connection
.dataAvailable() )
1035 while ( _Connection
.dataAvailable() )// && _TotalMessages<5)
1038 _DecodedHeader
= false;
1039 CBitMemStream
msgin (true);
1040 if (buildStream(msgin
) && decodeHeader(msgin
))
1045 msgin
.serial(message
);
1049 case SYSTEM_SYNC_CODE
:
1050 // receive sync, decode sync
1051 _ConnectionState
= Synchronize
;
1052 nldebug("CNET[%p]: login->synchronize", this);
1053 receiveSystemSync(msgin
);
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
);
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
);
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
1077 //msgin.displayStream("DBG:BEN:stateLogin:msgin");
1078 nlwarning("CNET[%p]: received system %d in state Login", this, message
);
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)
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
1117 void CNetworkConnection::receiveSystemSync(CBitMemStream
&msgin
)
1119 #ifdef ENABLE_INCOMING_MSG_RECORDER
1120 if ( _ReplayIncomingMessagesOn
)
1122 TGameCycle dummyTick
;
1124 msgin
.serial( dummyTick
);
1125 msgin
.serial( dummyTime
);
1130 _LatestSyncTime
= _UpdateTime
;
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
);
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;
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;
1180 //#pragma message ("FULL_FREQUENCY_SENDING_TO_CLIENT")
1181 _CurrentServerTick
= _Synchronize
+_CurrentReceivedNumber
;
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
)
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);
1215 _LatestSyncTime
= _UpdateTime
;
1217 //nlinfo("CNET[%p]: sent ACK_SYNC, _LastReceivedNumber=%d _LastAckInLongAck=%d", this, _LastReceivedNumber, _LastAckInLongAck);
1219 /* // display long ack
1221 uint bfsize = _LongAckBitField.size();
1224 static const char htable[] = "0123456789ABCDEF";
1225 for (i=0; i<bfsize; ++i)
1227 if (i>0 && (i&3)==0)
1229 buffer += htable[bbuffer];
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
)
1250 while ( (_ReplayIncomingMessagesOn
&& dataToReplayAvailable()) ||
1251 _Connection
.dataAvailable() )
1253 while (_Connection
.dataAvailable())// && _TotalMessages<5)
1256 _DecodedHeader
= false;
1257 CBitMemStream
msgin (true);
1258 if (buildStream(msgin
) && decodeHeader(msgin
))
1263 msgin
.serial(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
);
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
);
1282 case SYSTEM_SYNC_CODE
:
1283 // receive sync, decode sync
1284 receiveSystemSync(msgin
);
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
1292 nlwarning("CNET[%p]: received system %d in state Synchronize", this, message
);
1298 _ConnectionState
= Connected
;
1299 //nlwarning("CNET[%p]: synchronize->connected", this);
1300 _Changes
.push_back(CChange(0, ConnectionReady
));
1301 _ImpulseDecoder
.reset();
1302 receiveNormalMessage(msgin
);
1308 // send ack sync if received sync or last sync timed out
1309 if (_UpdateTime
-_LatestSyncTime
> 300)
1310 sendSystemAckSync();
1316 #ifdef SHOW_PROPERTIES_RECEIVED
1317 uint8 propReceived
[18];
1319 #ifdef MEASURE_RECEIVE_DATES
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();
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
)
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
;
1371 nlassert(_CurrentReceivedNumber
+_Synchronize
> _CurrentServerTick
);
1372 _CurrentServerTick
= _CurrentReceivedNumber
+_Synchronize
;
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
) )
1384 if (_PacketStamps
.empty() || _PacketStamps
.front().first
> _LastReceivedAck
)
1387 //nlwarning("Frontend ack'ed message %d not stamp dated", _LastReceivedAck);
1391 // get the send time of the acknowledged packet
1392 TTime ackedPacketTime
= _PacketStamps
.front().second
;
1395 uint32 ping
= (uint32
)(_UpdateTime
-ackedPacketTime
);
1396 _InstantPing
= ping
;
1397 if (ping
< _BestPing
)
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
);
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 ();
1449 // Decode the actions received in the impulsions
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
);
1464 case ACTION_GENERIC_CODE
:
1466 genericAction((CActionGeneric
*)actions
[i
]);
1469 case ACTION_GENERIC_MULTI_PART_CODE
:
1471 genericAction((CActionGenericMultiPart
*)actions
[i
]);
1474 case ACTION_DUMMY_CODE
:
1476 CActionDummy
*dummy
= ((CActionDummy
*)actions
[i
]);
1477 nldebug("CNET[%d] Received Dummy %d", this, dummy
->Dummy1
);
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()));
1496 #ifdef SHOW_PROPERTIES_RECEIVED
1498 string str
= "Received: ";
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];
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() );
1527 void CNetworkConnection::decodeVisualProperties( CBitMemStream
& msgin
)
1531 //nldebug( "pos: %d len: %u", msgin.getPos(), msgin.length() );
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 )
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() )
1559 _PropertyDecoder
.removeEntity( slot
);
1561 CChange
theChange( slot
, RemoveOldEntity
);
1562 _Changes
.push_back( theChange
);
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 );
1583 timestamp
= _CurrentServerTick
;
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
];
1601 * Set into property database
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));
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]);
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;
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
1696 if ( LogReceiveEnabled
&& (WatchedEntitySlot
== 256) || (WatchedEntitySlot
== slot
) )
1698 TRDateState
ds( timestamp
, predictedInterval
, currentTime
);
1699 ReceivePosDateLog
[slot
].push_back( ds
);
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
];
1721 if (_DataBase
!= NULL
&& (!IgnoreEntityDbUpdates
|| slot
==0))
1723 CCDBNodeBranch
*nodeRoot
;
1724 nodeRoot
= dynamic_cast<CCDBNodeBranch
*>(_DataBase
->getNode(0));
1727 CCDBNodeLeaf
*node
= dynamic_cast<CCDBNodeLeaf
*>(nodeRoot
->getNode(slot
)->getNode( PROPERTY_ORIENTATION
));
1728 nlassert(node
!= NULL
);
1729 node
->setValue64(ac
->getValue());
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
)
1772 msgin
.serial(listSize
);
1774 TargetSlotsList
.resize(listSize
);
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));
1785 CCDBNodeBranch
*nodeEntity
= dynamic_cast<CCDBNodeBranch
*>(nodeRoot
->getNode(slot
));
1786 nlassert(nodeEntity
!= NULL
);
1788 uint writeProp
= PROPERTY_TARGET_LIST
;
1797 TargetSlotsList
.push_back(INVALID_SLOT
);
1801 CCDBNodeLeaf
*nodeProp
= NULL
;
1805 for (i
=0; i
<listSize
; ++i
)
1810 value
+= (((uint64
)TargetSlotsList
[i
]) << (place
*8));
1815 nodeProp
= dynamic_cast<CCDBNodeLeaf
*>(nodeEntity
->getNode(writeProp
));
1816 nlassert(nodeProp
!= NULL
);
1817 nodeProp
->setValue64(value
);
1825 nodeProp
= dynamic_cast<CCDBNodeLeaf
*>(nodeEntity
->getNode(writeProp
));
1826 nlassert(nodeProp
!= NULL
);
1827 nodeProp
->setValue64(value
);
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
);
1843 CActionSint64
*ac
= (CActionSint64
*)CActionFactory::getInstance()->createByPropIndex( slot
, propIndex
);
1844 ac
->unpack( msgin
);
1846 #ifdef SHOW_PROPERTIES_RECEIVED
1847 ++propReceived
[propIndex
];
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() )
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
1875 bool aliasBit
= false;
1876 msgin
.serialBitAndLog( aliasBit
);
1878 msgin
.serialAndLog1( alias
);
1881 CChange
thechange( slot
, AddNewEntity
);
1882 thechange
.NewEntityInfo
.DataSetIndex
= (TClientDataSetIndex
)((ac
->getValue() >> 32) & 0xffffffff);
1883 thechange
.NewEntityInfo
.Alias
= alias
;
1884 _Changes
.push_back( thechange
);
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));
1904 CCDBNodeLeaf
*node
= dynamic_cast<CCDBNodeLeaf
*>(nodeRoot
->getNode(slot
)->getNode( propIndex
));
1905 nlassert(node
!= NULL
);
1906 node
->setValue64(ac
->getValue() & 0xFF); // (8 bits)
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
)
1919 if (_DataBase
!= NULL
&& (!IgnoreEntityDbUpdates
|| slot
==0))
1921 CCDBNodeBranch
*nodeRoot
;
1922 nodeRoot
= dynamic_cast<CCDBNodeBranch
*>(_DataBase
->getNode(0));
1925 CCDBNodeLeaf
*node
= dynamic_cast<CCDBNodeLeaf
*>(nodeRoot
->getNode(slot
)->getNode( PROPERTY_ORIENTATION
));
1926 nlassert(node
!= NULL
);
1927 node
->setValue64( (mode44
>> 12) /*&& 0xFFFFFFFF*/ );
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
1941 _PropertyDecoder
.decodeAbsPos2D( x
, y
, x16
, y16
);
1943 CCDBNodeBranch
*nodeRoot
;
1944 nodeRoot
= dynamic_cast<CCDBNodeBranch
*>(_DataBase
->getNode(0));
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
);
1958 nldebug( "%u: S%hu: Received mode with null pos", _CurrentServerTick
, (uint16
)slot
); // TEMP
1964 /* case PROPERTY_GUILD_SYMBOL:
1965 nlinfo("GuildSymbol received...");
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
;
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));
1987 CCDBNodeLeaf
*node
= dynamic_cast<CCDBNodeLeaf
*>(nodeRoot
->getNode(slot
)->getNode( propIndex
));
1988 nlassert(node
!= NULL
);
1989 node
->setValue64(ac
->getValue());
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
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];
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];
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() );
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
);
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)
2073 // Prevent to send a message too big
2074 //if (message.getPosInBit() + (*itblock).bitSize() > FrontEndInputBufferSize) // hard version
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
);
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
2095 //_PropertyDecoder.send (_CurrentSendNumber, _LastReceivedNumber);
2096 uint32 length
= message
.length();
2097 _Connection
.send (message
.buffer(), length
);
2098 //sendUDP (&(_Connection), message.buffer(), 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
)
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
;
2124 if ( (diff
> 3000) && (! _Connection
.dataAvailable()) )
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())
2146 #ifdef ENABLE_INCOMING_MSG_RECORDER
2147 if ( ClientCfg
.Local
&& !_ReplayIncomingMessagesOn
)
2149 while ( (_ReplayIncomingMessagesOn
&& dataToReplayAvailable()) ||
2150 _Connection
.dataAvailable() )
2152 while (_Connection
.dataAvailable())// && _TotalMessages<5)
2155 _DecodedHeader
= false;
2156 CBitMemStream
msgin (true);
2158 if (buildStream(msgin
) && decodeHeader(msgin
))
2163 msgin
.serial(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),
2180 _LastReceivedNumber = 0xffffffff;
2182 //nldebug("CNET[%p]: connected->probe", this);
2183 _Changes
.push_back(CChange(0, ProbeReceived
));
2184 receiveSystemProbe(msgin
);
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
);
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
);
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
2207 nlwarning("CNET[%p]: received system %d in state Connected", this, message
);
2213 receiveNormalMessage(msgin
);
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
);
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);
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
2271 // sends System ACK_PROBE
2272 #ifdef ENABLE_INCOMING_MSG_RECORDER
2273 if ( ClientCfg
.Local
&& !_ReplayIncomingMessagesOn
)
2275 while ( (_ReplayIncomingMessagesOn
&& dataToReplayAvailable()) ||
2276 _Connection
.dataAvailable() )
2278 while (_Connection
.dataAvailable())// && _TotalMessages<5)
2281 _DecodedHeader
= false;
2282 CBitMemStream
msgin (true);
2283 if (buildStream(msgin
) && decodeHeader(msgin
))
2288 msgin
.serial(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
);
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
);
2306 case SYSTEM_PROBE_CODE
:
2307 // receive sync, decode sync
2308 receiveSystemProbe(msgin
);
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
2316 nlwarning("CNET[%p]: received system %d in state Probe", message
);
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
;
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
)
2364 while ( (_ReplayIncomingMessagesOn
&& dataToReplayAvailable()) ||
2365 _Connection
.dataAvailable() )
2367 while (_Connection
.dataAvailable())// && _TotalMessages<5)
2370 _DecodedHeader
= false;
2371 CBitMemStream
msgin (true);
2372 if (buildStream(msgin
) && decodeHeader(msgin
))
2377 msgin
.serial(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
);
2388 case SYSTEM_PROBE_CODE
:
2389 // receive sync, decode sync
2390 _ConnectionState
= Probe
;
2391 nldebug("CNET[%p]: stalled->probe", this);
2392 receiveSystemProbe(msgin
);
2394 case SYSTEM_STALLED_CODE
:
2395 // receive stalled, decode stalled
2396 receiveSystemStalled(msgin
);
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
2404 nlwarning("CNET[%p]: received system %d in state Stalled", message
);
2410 nlwarning("CNET[%p]: received normal in state Stalled", this);
2423 // encoding / decoding methods
2426 bool CNetworkConnection::decodeHeader(CBitMemStream
&msgin
, bool checkMessageNumber
)
2431 // simulate packet loss
2433 if(uint((rand()%100)) < ClientCfg
.SimulatePacketLossRatio
)
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 ());
2453 //nlinfo("DBG:BEN: decodeHeader, packet=%d, %s", _CurrentReceivedNumber, _SystemMode ? "SYSTEM" : "NORMAL");
2461 msgin
.serial (_LastReceivedAck
);
2463 #ifdef INCLUDE_FE_STATS_IN_PACKETS
2464 // receive debug info in the message header
2469 float PriorityAmount
;
2470 uint16 SeenEntities
;
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);
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;
2490 static sint counter
= 128;
2494 // nlinfo( "User:%u Cycle:%u Rcv:%u Snd:%u PrioAmount:%.2f",
2495 // UserLWatch, CycleWatch, ReceiveWatch, SendWatch, PriorityAmount );
2498 #endif // DISPLAY_ENTITIES
2501 if (!checkMessageNumber
)
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
);
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
);
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);
2540 _AckBitMask
= (_CurrentReceivedNumber
- _LastReceivedNumber
== 32 && _LastAckBit
!= 0) ? 0x80000000 : 0x00000000;
2543 _LastAckBit
= ackBit
;
2545 // encode long ack bitfield
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;
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...
2598 uint bytelen
= msg
.length();
2599 sint32 impulseMinBitSize
= (sint32
)CActionFactory::getInstance ()->size( ag
);
2600 sint32 impulseBitSize
= impulseMinBitSize
+ (4 + bytelen
)*8;
2602 if (impulseBitSize
< maxImpulseBitSize
)
2609 CAction
*casted
= ag
;
2610 CActionFactory::getInstance()->remove(casted
);
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)
2619 nlassert( availableSize
> 0 ); // the available size must be larger than the 'empty' size
2621 sint32 nbBlock
= (bytelen
+ availableSize
- 1) / availableSize
;
2623 uint8 num
= _ImpulseMultiPartNumber
++;
2625 for (sint32 i
= 0; i
< nbBlock
; i
++)
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
);
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
);
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;
2650 ats
->TargetOrPickup
= (uint32
)targetOrPickup
;
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
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();
2690 CActionBlock
&block
= _Actions
.back();
2692 CAction *dummy = CActionFactory::getInstance()->create(INVALID_SLOT, ACTION_DUMMY_CODE);
2693 ((CActionDummy*)dummy)->Dummy1 = _DummySend++;
2697 _Actions
.back().Cycle
= cycle
;
2699 // check last block isn't bigger than maximum allowed
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)
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
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);
2800 updateBufferizedPackets ();
2801 nlinfo("CNET[%p]: sent DISCONNECTION", this);
2804 void CNetworkConnection::disconnect()
2806 #ifdef MEASURE_RECEIVE_DATES
2807 if ( LogReceiveEnabled
)
2809 displayReceiveLog();
2813 if (_ConnectionState
== NotInitialised
||
2814 _ConnectionState
== NotConnected
||
2815 _ConnectionState
== Authenticate
||
2816 _ConnectionState
== Disconnect
)
2818 //nlwarning("Unable to disconnect(): not connected yet, or already disconnected.");
2822 sendSystemDisconnection();
2823 _Connection
.close();
2824 _ConnectionState
= Disconnect
;
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
)
2846 while ( (_ReplayIncomingMessagesOn
&& dataToReplayAvailable()) ||
2847 _Connection
.dataAvailable() )
2849 while (_Connection
.dataAvailable())// && _TotalMessages<5)
2852 _DecodedHeader
= false;
2853 CBitMemStream
msgin (true);
2854 if (buildStream(msgin
) && decodeHeader(msgin
, false))
2859 msgin
.serial(message
);
2863 case SYSTEM_SYNC_CODE
:
2864 // receive sync, decode sync and state synchronize
2866 _ConnectionState
= Synchronize
;
2867 nldebug("CNET[%p]: quit->synchronize", this);
2868 receiveSystemSync(msgin
);
2872 case SYSTEM_PROBE_CODE:
2873 // receive sync, decode sync
2874 _ConnectionState = Probe;
2875 nldebug("CNET[%p]: stalled->probe", this);
2876 receiveSystemProbe(msgin);
2878 case SYSTEM_STALLED_CODE:
2879 // receive stalled, decode stalled
2880 _ConnectionState = Stalled;
2881 receiveSystemStalled(msgin);
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
2890 case SYSTEM_ACK_QUIT_CODE
:
2891 // receive ack quit -> reset connection state
2892 receiveSystemAckQuit(msgin
);
2895 nlwarning("CNET[%p]: received system %d in state Stalled", message
);
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)
2910 _LatestQuitTime
= _UpdateTime
;
2917 * Quit the game till the connection is reset
2919 void CNetworkConnection::quit()
2921 nlinfo("CNetworkConnection::quit() called, setting to quitting state.");
2924 _ConnectionState
= Quit
;
2925 _ReceivedAckQuit
= false;
2926 _LatestQuitTime
= _UpdateTime
;
2931 void CNetworkConnection::reset()
2933 _CurrentSendNumber
= 0;
2934 _LastReceivedNumber
= 0;
2935 _LastReceivedTime
= 0;
2936 _LastReceivedNormalTime
= 0;
2941 _InstantPing
= 10000;
2944 _MachineTimeAtTick
= ryzomGetLocalTime ();
2945 _MachineTicksAtTick
= CTime::getPerformanceTime();
2949 _PropertyDecoder
.init (256);
2952 _LongAckBitField
.resize(1024);
2953 _LastAckInLongAck
= 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;
2979 void CNetworkConnection::reinit()
2982 _ImpulseDecoder
.reset();
2984 _DataBase
->resetData(_CurrentServerTick
, true);
2985 _LongAckBitField
.clearAll();
2986 _PacketStamps
.clear();
2989 _GenericMultiPartTemp
.clear();
2994 // Reuse the udp socket
2995 _Connection
.~CUdpSimSock();
3000 new (&_Connection
) CUdpSimSock();
3003 #define new DEBUG_NEW
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);
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()
3038 sprintf(buf
, "CNET[%p]: %u queued blocks, %u changes", this, (uint
)_Actions
.size(), (uint
)_Changes
.size());
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
;
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);
3084 Temp
[agmp
->Part
] = agmp
->PartCont
;
3085 BlockReceived
[agmp
->Part
] = true;
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);
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") )
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
);
3187 // ***************************************************************************
3188 sint64
CNetworkConnection::convertToSmoothServerTick(NLMISC::TGameCycle t
) const
3190 return t
*SMOOTH_SERVER_TICK_PER_TICK
;
3194 // ***************************************************************************
3195 void CNetworkConnection::updateSmoothServerTick()
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
;
3218 // Compute the Factor of acceleration according to error difference (FIXED 16:16)
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
)
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
3236 float f
= (float)(errorDiff
-SMOOTH_SERVER_TICK_WINDOW
)/(SMOOTH_SERVER_TICK_DIFF_MAX
-SMOOTH_SERVER_TICK_WINDOW
);
3238 factor
= sint64(f
*65536);
3241 // Update the Smooth
3242 _CurrentSmoothServerTick
+= ((deltaT
*SMOOTH_SERVER_TICK_PER_TICK
*factor
)/getMsPerTick()) >> 16;