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) 2015-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/>.
23 #ifndef NL_NETWORK_CONNECTION_H
24 #define NL_NETWORK_CONNECTION_H
26 #include "nel/misc/types_nl.h"
27 #include "nel/misc/bit_set.h"
35 #include <nel/misc/bit_mem_stream.h>
36 #include <nel/net/inet_address.h>
37 #include <nel/net/login_cookie.h>
38 #include <nel/net/login_client.h>
39 #include <nel/net/udp_sim_sock.h>
40 #include <nel/misc/vectord.h>
41 #include <nel/misc/md5.h>
43 #include "time_client.h"
44 #include "game_share/entity_types.h"
45 #include "property_decoder.h"
46 #include "impulse_decoder.h"
47 #include "game_share/action.h"
48 #include "game_share/ryzom_entity_id.h"
49 #include "game_share/action_block.h"
50 #include "game_share/loot_harvest_state.h"
53 * ENABLE_INCOMING_MSG_RECORDER for recording/replaying incoming network messages
55 #undef ENABLE_INCOMING_MSG_RECORDER
57 #ifdef ENABLE_INCOMING_MSG_RECORDER
58 #include <nel/misc/file.h>
63 const uint PropertiesPerEntity = 16;
64 const uint EntitiesPerClient = 256;
74 class CActionGenericMultiPart
;
78 H_AUTO_DECL ( RZ_Client_Network_Connection_Set_Reference_Position
)
80 //#define MEASURE_FE_SENDING
82 extern const char * ConnectionStateCStr
[9];
86 * Exception thrown when the sending returns a "blocking operation interrupted".
88 class EBlockedByFirewall
: public NLNET::ESocket
96 DataSetIndex
= CLFECOMMON::INVALID_CLIENT_DATASET_INDEX
;
100 /// Compressed dataset row
101 CLFECOMMON::TClientDataSetIndex DataSetIndex
;
103 /// Alias (only for mission giver NPCs)
109 // ***************************************************************************
111 * Abstracts the connection client towards front-end
112 * \author Benjamin Legros, Olivier Cado
113 * \author Nevrax France
116 class CNetworkConnection
122 * DO LEAVE ENOUGH ROOM FOR FUTURE PROPERTIES !
132 struct TVPNodeClient
: public CLFECOMMON::TVPNodeBase
136 CNetworkConnection
*NetworkConnection
;
137 CLFECOMMON::TCLEntityId Slot
;
138 NLMISC::TGameCycle Timestamp
;
142 static TSlotContext SlotContext
;
145 TVPNodeClient() : TVPNodeBase() {}
147 TVPNodeClient
*a() { return (TVPNodeClient
*)VPA
; }
148 TVPNodeClient
*b() { return (TVPNodeClient
*)VPB
; }
149 TVPNodeClient
*parent() { return (TVPNodeClient
*)VPParent
; }
152 void decodeDiscreetProperties( NLMISC::CBitMemStream
& msgin
)
154 msgin
.serialBitAndLog( BranchHasPayload
);
155 if ( BranchHasPayload
)
159 SlotContext
.NetworkConnection
->decodeDiscreetProperty( msgin
, PropIndex
);
163 if ( a() ) a()->decodeDiscreetProperties( msgin
);
164 if ( b() ) b()->decodeDiscreetProperties( msgin
);
171 VPA
= new TVPNodeClient();
172 VPA
->VPParent
= this;
173 VPB
= new TVPNodeClient();
174 VPB
->VPParent
= this;
177 void makeDescendants( uint nbLevels
)
182 a()->makeDescendants( nbLevels
-1 );
183 b()->makeDescendants( nbLevels
-1 );
187 void deleteBranches()
190 { a()->deleteBranches();
194 { b()->deleteBranches();
202 { a()->deleteBranches();
210 { b()->deleteBranches();
217 /// Additional info for position updates
221 * Only for positions (property index 0): interval, in game cycle unit, between the current
222 * position update and the next predicted one. In most cases the next real position update
223 * will occur just before the predicted interval elapses.
225 * If the interval cannot be predicted yet (e.g. before having received two updates
226 * for the same entity), PredictedInterval is set to ~0.
228 NLMISC::TGameCycle PredictedInterval
;
230 /// Is position interior (only for position)
242 CChange( CLFECOMMON::TCLEntityId id
= 0, uint8 prop
= 255, NLMISC::TGameCycle gc
= 0 ) :
243 ShortId(id
), Property(prop
), GameCycle(gc
) {}
246 CLFECOMMON::TCLEntityId ShortId
;
251 /// The tick when the property changed, in server time
252 NLMISC::TGameCycle GameCycle
;
254 // These contextual additional properties must be set by hand (not by constructor)
257 /// Position additional information (prediction & interior)
258 TPositionInfo PositionInfo
;
260 /// Additional information when creating an entitys
261 TNewEntityInfo NewEntityInfo
;
266 /// The states of the connection to the server (if you change them, change ConnectionStateCStr)
267 enum TConnectionState
269 NotInitialised
= 0, // nothing happened yet
270 NotConnected
, // init() called
271 Authenticate
, // connect() called, identified by the login server
272 Login
, // connecting to the frontend, sending identification
273 Synchronize
, // connection accepted by the frontend, synchronizing
274 Connected
, // synchronized, connected, ready to work
275 Probe
, // connection lost by frontend, probing for response
276 Stalled
, // server is stalled
277 Disconnect
, // disconnect() called, or timeout, or connection closed by frontend
278 Quit
// quit() called
281 typedef void (*TImpulseCallback
)(NLMISC::CBitMemStream
&, CLFECOMMON::TPacketNumber packet
, void *arg
);
283 static bool LoggingMode
;
288 CNetworkConnection();
291 virtual ~CNetworkConnection();
293 /// @name Connection initialisation
297 * Inits the client connection to the front-end.
298 * \param cookie it s the cookie in string format that was passed to the exe command line (given by the nel_launcher)
299 * \param addr it s the front end address in string format that was passed to the exe command line (given by the nel_launcher)
301 void init(const std::string
&cookie
, const std::string
&addr
);
304 * Sets the cookie and front-end address, resets the connection state.
306 void initCookie(const std::string
&cookie
, const std::string
&addr
);
309 * Connects to the front-end (using or not the login system, depending on what was specified at init())
310 * \param result the message returned in case of an error
311 * Returns true if no error occurred.
313 bool connect(std::string
&result
);
317 * Sets the callback called when a generic impulse comes
319 void setImpulseCallback(TImpulseCallback callback
, void *argument
= NULL
);
324 /// @name Connection management
328 * Tells if the client is yet connected or not
334 * Gets the connection state
336 TConnectionState
getConnectionState() const { return _ConnectionState
; }
340 * Updates the whole connection with the frontend.
342 * Behaviour in login state when a firewall does not grant the sending:
343 * - When a sending is refused (error "Blocking operation interrupted")
344 * the exception EBlockedByFirewall in thrown. The first time, a time-out is armed.
345 * - In a later attempt, if the time-out expired, the function sets state to
346 * Disconnect, then throws EBlockedByFirewall.
348 * \return bool 'true' if data were sent/received.
354 * Disconnects the current connection
362 * Quit the game till the connection is reset
367 /// @name Database management
371 * Set database entry point
373 void setDataBase(NLMISC::CCDBNodeBranch
*database
);
379 /// @name Push and Send
383 * Buffers a bitmemstream, that will be converted into a generic action, to be sent later to the server (at next update).
385 void push (NLMISC::CBitMemStream
&msg
);
388 * Buffers an action to be sent at next update (or later updates if network overload occurs)
390 void push(CLFECOMMON::CAction
*action
);
393 * Buffers a target action (set targetOrPickup to true for target, false for pick-up
395 void pushTarget(CLFECOMMON::TCLEntityId slot
, LHSTATE::TLHState targetOrPickup
);
400 void send(NLMISC::TGameCycle
);
403 * Send last information without changes (only acknowedges frontend)
408 * Clear not acknownledged actions in sending buffer
410 void flushSendBuffer() { _Actions
.clear(); }
413 * Set player reference position
415 // void setReferencePosition(const NLMISC::CVectorD &position) { _ImpulseDecoder.setReferencePosition(position); }
420 /// @name Temp methods
423 const std::vector
<CChange
> &getChanges() { return _Changes
; }
425 void clearChanges() { _Changes
.clear(); }
430 /// @name Client time hints
432 NLMISC::TTime
getCurrentClientTime() { return _CurrentClientTime
; } // the time currently played at this frame (in the past)
433 NLMISC::TGameCycle
getCurrentClientTick() { return _CurrentClientTick
; } // the tick for the current frame (also in the past)
434 NLMISC::TGameCycle
getCurrentServerTick() { return _CurrentServerTick
; } // the last tick sent by the server.
435 NLMISC::TTime
getMachineTimeAtTick() { return _MachineTimeAtTick
; } // the machine time at the beginning of the current tick
436 NLMISC::TTicks
getMachineTicksAtTick() { return _MachineTicksAtTick
; }
437 sint32
getMsPerTick() { return _MsPerTick
; }
438 NLMISC::TGameCycle
getSynchronize() { return _Synchronize
; }
439 uint32
getBestPing() { return _BestPing
; }
440 uint32
getPing() const { return _InstantPing
; }
441 NLMISC::TTime
getLCT() { return _LCT
; } // Lag compensation time
442 NLMISC::TTime
getUpdateTime() { return _UpdateTime
; }
443 NLMISC::TTicks
getUpdateTicks() { return _UpdateTicks
; }
444 // Return an estimated smooth server tick. Increment only and accelerate/decelerate according to network
445 sint64
getCurrentSmoothServerTick() const { return _CurrentSmoothServerTick
; }
446 // Convert a GameCycle to a SmoothServerTick
447 sint64
convertToSmoothServerTick(NLMISC::TGameCycle t
) const;
451 /// Set the Lag Compensation Time in ms (default: 1000)
452 void setLCT( NLMISC::TTime lct
) { _LCT
= lct
; }
454 /// Allocation statistics
455 void displayAllocationStats();
457 /// Get Allocation statistics
458 std::string
getAllocationStats();
460 /// @name Network Stats Methods
463 uint32
getSentBytes() { return _TotalSentBytes
; }
464 uint32
getPartialSentBytes() { uint32 ret
= _PartialSentBytes
; _PartialSentBytes
= 0; return ret
; }
465 uint32
getReceivedBytes() { return _TotalReceivedBytes
; }
466 uint32
getPartialReceivedBytes() { uint32 ret
= _PartialReceivedBytes
; _PartialReceivedBytes
= 0; return ret
; }
467 bool getConnectionQuality() { return _ConnectionQuality
; }
468 NLMISC::TTime
getTimeSinceLastReceivedPacket() { return NLMISC::CTime::getLocalTime()-_LastReceivedNormalTime
; }
471 /// Return the average billed download rate in kbps, including all headers (UDP+IP+Ethernet)
472 float getMeanDownload() { return (_MeanDownload
.mean(ryzomGetLocalTime ())+20+8+14)*0.008f
; }
474 /// Return the average billed upload rate in kbps, including all headers (UDP+IP+Ethernet)
475 float getMeanUpload() { return (_MeanUpload
.mean(ryzomGetLocalTime ())+20+8+14)*0.008f
; }
477 /// Return the packet loss average
478 float getMeanPacketLoss()
480 NLMISC::TTime time
= ryzomGetLocalTime ();
481 _MeanPackets
.check(time
);
482 _MeanLoss
.check(time
);
483 float dpk
= _MeanPackets
.Values
.empty() ? 0.0f
: _MeanPackets
.Values
.back().second
-_MeanPackets
.Values
.front().second
+1;
484 float tloss
= _MeanLoss
.Content
;
485 return (dpk
> 0.0f
) ? std::min(100.0f
, tloss
*100.0f
/dpk
) : 0.0f
;
488 /// Get total lost packets
489 uint32
getTotalLostPackets() const { return _TotalLostPackets
; }
494 /// Get the NLMISC::CEntityId from the TCLEntityId
495 CLFECOMMON::TSheetId
get(CLFECOMMON::TCLEntityId id
) const { return _PropertyDecoder
.sheet(id
); }
497 /// Get the TCLEntityId from the NLMISC::CEntityId
498 sint
get(CLFECOMMON::TSheetId id
) const
500 TIdMap::const_iterator it
= _IdMap
.find(id
);
501 return (it
!= _IdMap
.end()) ? (*it
).second
: -1;
504 /// Set player's reference position
505 void setReferencePosition(const NLMISC::CVectorD
&position
)
507 H_AUTO_USE ( RZ_Client_Network_Connection_Set_Reference_Position
)
508 _PropertyDecoder
.setReferencePosition( position
);
511 /// Get local IP address
512 const NLNET::CInetAddress
& getAddress()
514 return _Connection
.localAddr();
518 NLNET::CUdpSock
& getConnection()
520 return _Connection
.UdpSock
;
526 if(_LoginCookie
.isValid())
527 return _LoginCookie
.getUserId();
532 /// Get connection state c_string
533 const char * getConnectionStateCStr()
535 return ConnectionStateCStr
[_ConnectionState
];
538 /// Return the login cookie
539 const NLNET::CLoginCookie
& getLoginCookie() const
544 #ifdef ENABLE_INCOMING_MSG_RECORDER
545 /// Return 'true' if currently recording.
546 bool isRecording() const {return _RecordIncomingMessagesOn
;}
547 /// Return 'true' if currently replaying.
548 bool isReplaying() const {return _ReplayIncomingMessagesOn
;}
550 /// Start/stop recording the incoming messages (in online mode)
551 void setRecordingMode( bool onOff
, const std::string
& filename
= "");
553 /// Start/stop replaying the incoming messages (in offline mode)
554 void setReplayingMode( bool onOff
, const std::string
& filename
= "");
557 void decodeDiscreetProperty( NLMISC::CBitMemStream
& msgin
, CLFECOMMON::TPropIndex propIndex
);
559 const std::string
&getFrontendAddress() { return _FrontendAddress
; }
562 /// The current state of the connection
563 TConnectionState _ConnectionState
;
565 /// Connection Quality check
566 bool _ConnectionQuality
;
568 /// The address of the frontend service
569 std::string _FrontendAddress
;
571 /// The cookie for the login service
572 NLNET::CLoginCookie _LoginCookie
;
574 /// The UDP connection to the frontend
575 NLNET::CUdpSimSock _Connection
;
577 /// The receive buffer
578 uint32 _ReceiveBuffer
[65536];
580 /// The impulse decoder
581 CImpulseDecoder _ImpulseDecoder
;
583 /// The callback for generic actions
584 TImpulseCallback _ImpulseCallback
;
586 /// The callback argument
590 /// Position update tick queues (used for statistical interval prediction), one per slot
591 std::deque
<NLMISC::TGameCycle
> _PosUpdateTicks
[256];
593 /// Position update interval sorted sets (used for statistical interval prediction), one per slot
594 std::multiset
<NLMISC::TGameCycle
> _PosUpdateIntervals
[256];
596 /// MD5 hash keys of msg.xml and database.xml
597 NLMISC::CHashKeyMD5 _MsgXmlMD5
;
598 NLMISC::CHashKeyMD5 _DatabaseXmlMD5
;
599 // if prec don't work, those may (if the server run on windows, prec won't work)
600 NLMISC::CHashKeyMD5 _AltMsgXmlMD5
;
601 NLMISC::CHashKeyMD5 _AltDatabaseXmlMD5
;
604 /// @name Network statistics
610 CMeanComputer() : MeanPeriod(2000), Content(0.0f
) {}
611 std::deque
< std::pair
<NLMISC::TTime
, float> > Values
;
612 uint32 MeanPeriod
; // in ms
615 // checks all values have valid time (inside the mean period)
616 void check(NLMISC::TTime time
)
618 while (!Values
.empty() && Values
.front().first
< time
-MeanPeriod
)
620 Content
-= Values
.front().second
;
625 // updates the mean with a new value and time
626 void update(float value
, NLMISC::TTime time
)
628 if (!Values
.empty() && Values
.back().first
> time
)
632 Values
.push_back(std::make_pair(time
, value
));
637 float mean(NLMISC::TTime time
)
640 return Content
*1000.0f
/(float)MeanPeriod
;
644 /// The total received length (from the beginning)
645 uint32 _TotalReceivedBytes
;
647 /// The partial received length (since last read)
648 uint32 _PartialReceivedBytes
;
650 /// The total sent length
651 uint32 _TotalSentBytes
;
653 /// Partial sent length
654 uint32 _PartialSentBytes
;
656 /// Total lost packets
657 uint32 _TotalLostPackets
;
659 /// Mean download (payload bytes)
660 CMeanComputer _MeanDownload
;
662 /// Mean upload (payload bytes)
663 CMeanComputer _MeanUpload
;
666 CMeanComputer _MeanPackets
;
669 CMeanComputer _MeanLoss
;
674 /// The property decoder
675 CPropertyDecoder _PropertyDecoder
;
677 /// The database entry
678 NLMISC::CCDBNodeBranch
*_DataBase
;
681 /// @name Header message decoding
684 /// Was the header decoded for the last received message?
687 /// The received number lastly decoded
688 CLFECOMMON::TPacketNumber _CurrentReceivedNumber
;
690 /// The message is in system mode
693 /// Did we received a probe since last update
702 /// @name Client and Server packet numbering management
705 CLFECOMMON::TPacketNumber _CurrentSendNumber
; // number that will be used to send the next packet
707 CLFECOMMON::TPacketNumber _LastReceivedNumber
; // number of the last packet receive from the server
708 uint32 _AckBitMask
; // this mask contains ack of old messages received by the server
709 uint32 _LastAckBit
; // a remember of the status of the previous received packet
710 NLMISC::TTime _LastReceivedTime
; // last time the client received something from the server
712 NLMISC::CBitSet _LongAckBitField
;
713 CLFECOMMON::TPacketNumber _LastAckInLongAck
;
715 NLMISC::TGameCycle _LastSentCycle
;
717 CLFECOMMON::TPacketNumber _LastReceivedPacketInBothModes
;
719 NLMISC::TTime _LastReceivedNormalTime
; // last time the client received valid normal message from the server
721 uint8 _ImpulseMultiPartNumber
;
726 /// @name Client and Server time management
729 /// The stamps queues
730 typedef std::deque
<std::pair
<sint32
, NLMISC::TTime
> > TStampQueue
;
732 // used for lag compensation
733 NLMISC::TTime _CurrentClientTime
; // the time currently played at this frame (in the past)
734 NLMISC::TGameCycle _CurrentClientTick
; // the tick for the current frame (also in the past)
735 NLMISC::TGameCycle _CurrentServerTick
; // the last tick sent by the server.
736 NLMISC::TTime _MachineTimeAtTick
; // the machine time at the beginning of the current tick
737 NLMISC::TTicks _MachineTicksAtTick
;
739 NLMISC::TGameCycle _Synchronize
;
742 NLMISC::TTime _LCT
; // Lag compensation time
743 TStampQueue _PacketStamps
;
745 NLMISC::TTime _UpdateTime
;
746 NLMISC::TTicks _UpdateTicks
;
747 NLMISC::TTime _LastSentSync
;
749 NLMISC::TTime _LastSendTime
;
753 std::list
<CLFECOMMON::CActionBlock
> _Actions
;
756 std::vector
<CChange
> _Changes
;
761 static bool _Registered
;
763 /// @name Generic action multipart handling structures
765 struct CGenericMultiPartTemp
767 CGenericMultiPartTemp () : NbBlock(0xFFFFFFFF) { }
769 uint32 NbCurrentBlock
;
771 std::vector
<std::vector
<uint8
> > Temp
;
773 std::vector
<bool> BlockReceived
;
775 void set (CLFECOMMON::CActionGenericMultiPart
*agmp
, CNetworkConnection
*parent
);
778 std::vector
<CGenericMultiPartTemp
> _GenericMultiPartTemp
;
781 /// @name NLMISC::CEntityId handling (for gamedev)
785 enum { bucket_size
= 4, min_buckets
= 8, };
786 size_t operator () (const CLFECOMMON::TSheetId
&id
) const { return id
; }
787 inline bool operator() (const CLFECOMMON::TSheetId
&id1
, const CLFECOMMON::TSheetId
&id2
) const { return (size_t)id1
< (size_t)id2
; }
790 typedef CHashMap
<CLFECOMMON::TSheetId
, CLFECOMMON::TCLEntityId
, CHash
> TIdMap
;
796 TVPNodeClient
*_VisualPropertyTreeRoot
;
799 /// \name Smooth ServerTick mgt.
801 sint64 _CurrentSmoothServerTick
;
802 NLMISC::TTime _SSTLastLocalTime
;
804 void updateSmoothServerTick();
811 /// Set MsPerTick value
812 void setMsPerTick(sint32 msPerTick
);
815 /// Builds the CBitMemStream from the next incoming packet
816 bool buildStream(NLMISC::CBitMemStream
&msgin
);
818 /// @name Client automaton states and actions
822 void sendSystemLogin();
824 NLMISC::TTime _LatestLoginTime
;
828 void receiveSystemSync(NLMISC::CBitMemStream
&msgin
);
829 void sendSystemAckSync();
830 bool stateSynchronize();
831 NLMISC::TTime _LatestSyncTime
;
835 void receiveNormalMessage(NLMISC::CBitMemStream
&msgin
);
836 void sendNormalMessage();
837 bool stateConnected();
838 CLFECOMMON::TPacketNumber _LastReceivedAck
;
839 uint _NormalPacketsReceived
;
842 void receiveSystemProbe(NLMISC::CBitMemStream
&msgin
);
843 void sendSystemAckProbe();
845 std::vector
<CLFECOMMON::TPacketNumber
> _LatestProbes
;
846 CLFECOMMON::TPacketNumber _LatestProbe
;
847 NLMISC::TTime _LatestProbeTime
;
850 void receiveSystemStalled(NLMISC::CBitMemStream
&msgin
);
854 void sendSystemDisconnection();
858 void receiveSystemAckQuit(NLMISC::CBitMemStream
&msgin
);
859 void sendSystemQuit();
861 bool _ReceivedAckQuit
;
862 NLMISC::TTime _LatestQuitTime
;
871 bool decodeHeader(NLMISC::CBitMemStream
&msgin
, bool checkMessageNumber
= true);
874 void buildSystemHeader(NLMISC::CBitMemStream
&msgout
);
877 void genericAction (CLFECOMMON::CActionGeneric
*ag
);
878 void genericAction (CLFECOMMON::CActionGenericMultiPart
*agmp
);
881 void statsSend(uint32 bytes
);
882 void statsReceive(uint32 bytes
);
884 bool associationBitsHaveChanged( CLFECOMMON::TCLEntityId slot
, uint32 associationBits
)
886 bool res
= ((uint16
)associationBits
!= _PropertyDecoder
.associationBits( slot
));
887 _PropertyDecoder
.associationBits( slot
) = (uint16
)associationBits
;
891 void decodeVisualProperties( NLMISC::CBitMemStream
& msgin
);
893 /// Return true if there is some messages to replay (in replay mode)
894 bool dataToReplayAvailable();
896 uint32 _AssociationBits
;
898 #ifdef ENABLE_INCOMING_MSG_RECORDER
899 NLMISC::COFile _RecordedMessagesOut
;
901 NLMISC::CIFile _RecordedMessagesIn
;
902 NLMISC::TGameCycle _NextClientTickToReplay
; // ~0 when no more message
903 NLMISC::CBitMemStream _NextMessageToReplay
; // loaded at beginning of update(), read in buildStream()
905 bool _RecordIncomingMessagesOn
;
906 bool _ReplayIncomingMessagesOn
;
911 friend struct CGenericMultiPartTemp
;
915 #endif // NL_NETWORK_CONNECTION_H
917 /* End of network_connection.h */