2008-05-02 19:21:33 +00:00
/ *
* Copyright ( c ) Contributors , http : //opensimulator.org/
* See CONTRIBUTORS . TXT for a full list of copyright holders .
*
* Redistribution and use in source and binary forms , with or without
* modification , are permitted provided that the following conditions are met :
* * Redistributions of source code must retain the above copyright
* notice , this list of conditions and the following disclaimer .
* * Redistributions in binary form must reproduce the above copyright
* notice , this list of conditions and the following disclaimer in the
* documentation and / or other materials provided with the distribution .
2009-06-01 06:37:14 +00:00
* * Neither the name of the OpenSimulator Project nor the
2008-05-02 19:21:33 +00:00
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission .
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ` ` AS IS ' ' AND ANY
* EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT , INDIRECT , INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES
* ( INCLUDING , BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ;
* LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
* /
using System ;
using System.Collections.Generic ;
2018-01-22 17:09:38 +00:00
using System.Collections.Concurrent ;
2011-02-08 20:06:14 +00:00
using System.Diagnostics ;
2009-11-05 20:01:40 +00:00
using System.IO ;
2008-05-02 19:21:33 +00:00
using System.Net ;
using System.Net.Sockets ;
using System.Reflection ;
2009-10-06 00:38:14 +00:00
using System.Threading ;
2008-05-02 19:21:33 +00:00
using log4net ;
2008-11-03 18:33:35 +00:00
using Nini.Config ;
2009-02-12 09:53:12 +00:00
using OpenMetaverse.Packets ;
2008-05-02 19:21:33 +00:00
using OpenSim.Framework ;
2012-10-16 20:55:00 +00:00
using OpenSim.Framework.Console ;
2012-07-25 22:11:50 +00:00
using OpenSim.Framework.Monitoring ;
2009-02-06 16:55:34 +00:00
using OpenSim.Region.Framework.Scenes ;
2015-09-03 22:59:06 +00:00
using OpenSim.Region.Framework.Interfaces ;
2009-10-06 00:38:14 +00:00
using OpenMetaverse ;
2015-09-03 22:59:06 +00:00
using Mono.Addins ;
2009-10-21 01:19:17 +00:00
using TokenBucket = OpenSim . Region . ClientStack . LindenUDP . TokenBucket ;
2019-01-02 16:44:13 +00:00
2008-05-02 19:21:33 +00:00
namespace OpenSim.Region.ClientStack.LindenUDP
{
2009-10-06 19:13:16 +00:00
/// <summary>
/// A shim around LLUDPServer that implements the IClientNetworkServer interface
/// </summary>
2015-09-03 22:59:06 +00:00
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "LLUDPServerShim")]
2016-06-13 23:21:47 +00:00
public class LLUDPServerShim : INonSharedRegionModule
2008-05-02 19:21:33 +00:00
{
2016-06-13 23:21:47 +00:00
protected IConfigSource m_Config ;
2016-06-13 23:27:48 +00:00
protected LLUDPServer m_udpServer ;
2008-09-09 01:26:48 +00:00
2015-09-03 22:59:06 +00:00
#region INonSharedRegionModule
2016-06-13 23:21:47 +00:00
public virtual string Name
2015-09-03 22:59:06 +00:00
{
get { return "LLUDPServerShim" ; }
}
2016-06-13 23:21:47 +00:00
public virtual Type ReplaceableInterface
2015-09-03 22:59:06 +00:00
{
get { return null ; }
}
2016-06-13 23:21:47 +00:00
public virtual void Initialise ( IConfigSource source )
2015-09-03 22:59:06 +00:00
{
m_Config = source ;
}
2016-06-13 23:21:47 +00:00
public virtual void Close ( )
2015-09-03 22:59:06 +00:00
{
}
2016-06-13 23:21:47 +00:00
public virtual void AddRegion ( Scene scene )
2008-05-02 19:21:33 +00:00
{
2015-09-03 22:59:06 +00:00
uint port = ( uint ) scene . RegionInfo . InternalEndPoint . Port ;
IPAddress listenIP = scene . RegionInfo . InternalEndPoint . Address ;
2016-12-13 19:47:26 +00:00
Initialise ( listenIP , ref port , scene . RegionInfo . ProxyOffset , m_Config , scene . AuthenticateHandler ) ;
2015-09-03 22:59:06 +00:00
scene . RegionInfo . InternalEndPoint . Port = ( int ) port ;
AddScene ( scene ) ;
2008-05-02 19:21:33 +00:00
}
2016-06-13 23:21:47 +00:00
public virtual void RemoveRegion ( Scene scene )
2015-09-03 22:59:06 +00:00
{
Stop ( ) ;
}
2016-06-13 23:21:47 +00:00
public virtual void RegionLoaded ( Scene scene )
2015-09-03 22:59:06 +00:00
{
Start ( ) ;
}
# endregion
2016-12-13 19:47:26 +00:00
public virtual void Initialise ( IPAddress listenIP , ref uint port , int proxyPortOffsetParm , IConfigSource configSource , AgentCircuitManager circuitManager )
2008-05-02 19:21:33 +00:00
{
2016-12-13 19:47:26 +00:00
m_udpServer = new LLUDPServer ( listenIP , ref port , proxyPortOffsetParm , configSource , circuitManager ) ;
2008-05-02 19:21:33 +00:00
}
2016-06-13 23:21:47 +00:00
public virtual void AddScene ( IScene scene )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
m_udpServer . AddScene ( scene ) ;
2012-11-15 01:14:18 +00:00
2013-07-29 22:18:29 +00:00
StatsManager . RegisterStat (
new Stat (
"ClientLogoutsDueToNoReceives" ,
"Number of times a client has been logged out because no packets were received before the timeout." ,
"" ,
"" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
MeasuresOfInterest . None ,
stat = > stat . Value = m_udpServer . ClientLogoutsDueToNoReceives ,
StatVerbosity . Debug ) ) ;
2013-07-22 22:58:45 +00:00
StatsManager . RegisterStat (
new Stat (
"IncomingUDPReceivesCount" ,
2013-07-22 23:15:58 +00:00
"Number of UDP receives performed" ,
2013-07-29 22:18:29 +00:00
"" ,
2013-07-22 22:58:45 +00:00
"" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
MeasuresOfInterest . AverageChangeOverTime ,
stat = > stat . Value = m_udpServer . UdpReceives ,
StatVerbosity . Debug ) ) ;
2012-11-15 01:14:18 +00:00
StatsManager . RegisterStat (
new Stat (
"IncomingPacketsProcessedCount" ,
2013-07-22 22:30:09 +00:00
"Number of inbound LL protocol packets processed" ,
2013-07-29 22:18:29 +00:00
"" ,
2012-11-15 01:14:18 +00:00
"" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
2012-11-16 04:36:22 +00:00
MeasuresOfInterest . AverageChangeOverTime ,
2012-11-15 01:14:18 +00:00
stat = > stat . Value = m_udpServer . IncomingPacketsProcessed ,
StatVerbosity . Debug ) ) ;
2013-07-22 22:30:09 +00:00
2013-08-14 21:07:22 +00:00
StatsManager . RegisterStat (
new Stat (
"IncomingPacketsMalformedCount" ,
"Number of inbound UDP packets that could not be recognized as LL protocol packets." ,
"" ,
"" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
MeasuresOfInterest . AverageChangeOverTime ,
stat = > stat . Value = m_udpServer . IncomingMalformedPacketCount ,
StatVerbosity . Info ) ) ;
2013-08-14 21:33:12 +00:00
StatsManager . RegisterStat (
new Stat (
"IncomingPacketsOrphanedCount" ,
"Number of inbound packets that were not initial connections packets and could not be associated with a viewer." ,
"" ,
"" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
MeasuresOfInterest . AverageChangeOverTime ,
stat = > stat . Value = m_udpServer . IncomingOrphanedPacketCount ,
StatVerbosity . Info ) ) ;
2013-11-06 01:02:20 +00:00
StatsManager . RegisterStat (
new Stat (
"IncomingPacketsResentCount" ,
"Number of inbound packets that clients indicate are resends." ,
"" ,
"" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
MeasuresOfInterest . AverageChangeOverTime ,
stat = > stat . Value = m_udpServer . IncomingPacketsResentCount ,
StatVerbosity . Debug ) ) ;
2013-07-22 23:15:58 +00:00
StatsManager . RegisterStat (
new Stat (
"OutgoingUDPSendsCount" ,
"Number of UDP sends performed" ,
2013-07-29 22:18:29 +00:00
"" ,
2013-07-22 23:15:58 +00:00
"" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
MeasuresOfInterest . AverageChangeOverTime ,
stat = > stat . Value = m_udpServer . UdpSends ,
StatVerbosity . Debug ) ) ;
2013-10-31 23:45:52 +00:00
StatsManager . RegisterStat (
new Stat (
"OutgoingPacketsResentCount" ,
"Number of packets resent because a client did not acknowledge receipt" ,
"" ,
"" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
MeasuresOfInterest . AverageChangeOverTime ,
stat = > stat . Value = m_udpServer . PacketsResentCount ,
StatVerbosity . Debug ) ) ;
2013-07-22 22:30:09 +00:00
StatsManager . RegisterStat (
new Stat (
"AverageUDPProcessTime" ,
"Average number of milliseconds taken to process each incoming UDP packet in a sample." ,
"This is for initial receive processing which is separate from the later client LL packet processing stage." ,
"ms" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
MeasuresOfInterest . None ,
2015-07-24 10:05:57 +00:00
stat = > stat . Value = m_udpServer . AverageReceiveTicksForLastSamplePeriod ,
2017-01-05 19:07:37 +00:00
// stat =>
2015-07-24 10:05:57 +00:00
// stat.Value = Math.Round(m_udpServer.AverageReceiveTicksForLastSamplePeriod, 7),
2013-07-22 22:30:09 +00:00
StatVerbosity . Debug ) ) ;
2008-05-02 19:21:33 +00:00
}
2016-06-13 23:21:47 +00:00
public virtual bool HandlesRegion ( Location x )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
return m_udpServer . HandlesRegion ( x ) ;
2008-05-02 19:21:33 +00:00
}
2016-06-13 23:21:47 +00:00
public virtual void Start ( )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
m_udpServer . Start ( ) ;
2008-05-02 19:21:33 +00:00
}
2016-06-13 23:21:47 +00:00
public virtual void Stop ( )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
m_udpServer . Stop ( ) ;
2008-05-02 19:21:33 +00:00
}
2015-09-03 22:59:06 +00:00
2009-10-06 00:38:14 +00:00
}
2009-10-06 19:13:16 +00:00
/// <summary>
/// The LLUDP server for a region. This handles incoming and outgoing
/// packets for all UDP connections to the region
/// </summary>
2009-10-08 21:25:14 +00:00
public class LLUDPServer : OpenSimUDPBase
2009-10-06 00:38:14 +00:00
{
2013-07-04 16:29:53 +00:00
private static readonly ILog m_log = LogManager . GetLogger ( MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
2009-10-16 21:17:13 +00:00
/// <summary>Maximum transmission unit, or UDP packet size, for the LLUDP protocol</summary>
public const int MTU = 1400 ;
2019-03-10 11:23:38 +00:00
public const int MAXPAYLOAD = 1250 ;
2009-10-16 21:17:13 +00:00
2013-07-29 22:18:29 +00:00
/// <summary>Number of forced client logouts due to no receipt of packets before timeout.</summary>
2016-06-13 23:21:47 +00:00
public int ClientLogoutsDueToNoReceives { get ; protected set ; }
2013-07-29 22:18:29 +00:00
2013-07-04 16:29:53 +00:00
/// <summary>
/// Default packet debug level given to new clients
/// </summary>
public int DefaultClientPacketDebugLevel { get ; set ; }
2008-05-02 19:21:33 +00:00
2014-10-02 22:18:21 +00:00
/// <summary>
/// If set then all inbound agent updates are discarded. For debugging purposes.
/// discard agent update.
/// </summary>
public bool DiscardInboundAgentUpdates { get ; set ; }
2009-10-21 07:18:35 +00:00
/// <summary>The measured resolution of Environment.TickCount</summary>
public readonly float TickCountResolution ;
2012-10-12 00:39:37 +00:00
2009-10-23 17:35:47 +00:00
/// <summary>Number of texture packets to put on the queue each time the
/// OnQueueEmpty event is triggered for textures</summary>
public readonly int TextureSendLimit ;
2009-10-21 07:18:35 +00:00
2009-10-06 00:38:14 +00:00
/// <summary>Handlers for incoming packets</summary>
//PacketEventDictionary packetEvents = new PacketEventDictionary();
/// <summary>Incoming packets that are awaiting handling</summary>
2016-06-13 23:21:47 +00:00
//protected OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
2012-08-28 20:17:17 +00:00
2018-01-22 17:09:38 +00:00
protected BlockingCollection < IncomingPacket > packetInbox = new BlockingCollection < IncomingPacket > ( ) ;
2012-10-12 00:39:37 +00:00
2009-10-06 00:38:14 +00:00
/// <summary>Bandwidth throttle for this UDP server</summary>
2016-06-13 23:21:47 +00:00
public TokenBucket Throttle { get ; protected set ; }
2017-01-05 19:07:37 +00:00
2014-10-08 20:30:52 +00:00
/// <summary>Per client throttle rates enforced by this server</summary>
/// <remarks>
/// If the total rate is non-zero, then this is the maximum total throttle setting that any client can ever have.
/// The other rates (resend, asset, etc.) are the defaults for a new client and can be changed (and usually
/// do get changed immediately). They do not need to sum to the total.
/// </remarks>
2016-06-13 23:21:47 +00:00
public ThrottleRates ThrottleRates { get ; protected set ; }
2017-01-05 19:07:37 +00:00
2009-10-06 00:38:14 +00:00
/// <summary>Manages authentication for agent circuits</summary>
2016-06-13 23:21:47 +00:00
protected AgentCircuitManager m_circuitManager ;
2012-10-12 00:39:37 +00:00
2009-10-06 00:38:14 +00:00
/// <summary>Reference to the scene this UDP server is attached to</summary>
2016-06-13 23:21:47 +00:00
public Scene Scene { get ; protected set ; }
2012-10-12 00:39:37 +00:00
2009-10-06 00:38:14 +00:00
/// <summary>The X/Y coordinates of the scene this UDP server is attached to</summary>
2016-06-13 23:21:47 +00:00
protected Location m_location ;
2012-10-12 00:39:37 +00:00
2009-10-14 18:43:31 +00:00
/// <summary>The size of the receive buffer for the UDP socket. This value
/// is passed up to the operating system and used in the system networking
/// stack. Use zero to leave this value as the default</summary>
2016-06-13 23:21:47 +00:00
protected int m_recvBufferSize ;
2012-10-12 00:39:37 +00:00
2009-10-21 18:59:48 +00:00
/// <summary>Tracks whether or not a packet was sent each round so we know
/// whether or not to sleep</summary>
2016-06-13 23:21:47 +00:00
protected bool m_packetSent ;
2009-10-06 00:38:14 +00:00
2009-11-28 00:17:36 +00:00
/// <summary>Environment.TickCount of the last time that packet stats were reported to the scene</summary>
2016-06-13 23:21:47 +00:00
protected int m_elapsedMSSinceLastStatReport = 0 ;
2012-10-12 00:39:37 +00:00
2009-10-21 22:22:23 +00:00
/// <summary>Environment.TickCount of the last time the outgoing packet handler executed</summary>
2016-11-18 03:25:29 +00:00
protected double m_tickLastOutgoingPacketHandler ;
2012-10-12 00:39:37 +00:00
2009-10-21 22:22:23 +00:00
/// <summary>Keeps track of the number of elapsed milliseconds since the last time the outgoing packet handler looped</summary>
2016-11-18 03:25:29 +00:00
protected double m_elapsedMSOutgoingPacketHandler ;
2012-10-12 00:39:37 +00:00
2009-10-21 22:22:23 +00:00
/// <summary>Keeps track of the number of 100 millisecond periods elapsed in the outgoing packet handler executed</summary>
2016-06-13 23:21:47 +00:00
protected int m_elapsed100MSOutgoingPacketHandler ;
2012-10-12 00:39:37 +00:00
2009-10-21 22:22:23 +00:00
/// <summary>Keeps track of the number of 500 millisecond periods elapsed in the outgoing packet handler executed</summary>
2016-06-13 23:21:47 +00:00
protected int m_elapsed500MSOutgoingPacketHandler ;
2009-10-21 22:22:23 +00:00
/// <summary>Flag to signal when clients should check for resends</summary>
2012-06-08 02:12:23 +00:00
protected bool m_resendUnacked ;
2009-10-21 22:22:23 +00:00
/// <summary>Flag to signal when clients should send ACKs</summary>
2012-06-08 02:12:23 +00:00
protected bool m_sendAcks ;
2009-10-21 22:22:23 +00:00
/// <summary>Flag to signal when clients should send pings</summary>
2012-06-08 02:12:23 +00:00
protected bool m_sendPing ;
2009-10-21 22:22:23 +00:00
2016-06-13 23:21:47 +00:00
protected ExpiringCache < IPEndPoint , Queue < UDPPacketBuffer > > m_pendingCache = new ExpiringCache < IPEndPoint , Queue < UDPPacketBuffer > > ( ) ;
2014-09-14 01:28:42 +00:00
2016-06-13 23:21:47 +00:00
protected int m_defaultRTO = 0 ;
protected int m_maxRTO = 0 ;
protected int m_ackTimeout = 0 ;
protected int m_pausedAckTimeout = 0 ;
protected bool m_disableFacelights = false ;
2010-08-08 00:57:02 +00:00
2009-10-06 00:38:14 +00:00
public Socket Server { get { return null ; } }
2013-10-31 23:45:52 +00:00
/// <summary>
/// Record how many packets have been resent
/// </summary>
internal int PacketsResentCount { get ; set ; }
/// <summary>
/// Record how many packets have been sent
/// </summary>
internal int PacketsSentCount { get ; set ; }
2013-11-06 01:02:20 +00:00
/// <summary>
/// Record how many incoming packets are indicated as resends by clients.
/// </summary>
internal int IncomingPacketsResentCount { get ; set ; }
2013-08-14 21:07:22 +00:00
/// <summary>
/// Record how many inbound packets could not be recognized as LLUDP packets.
/// </summary>
2016-06-13 23:21:47 +00:00
public int IncomingMalformedPacketCount { get ; protected set ; }
2011-07-27 06:35:19 +00:00
2013-08-14 21:33:12 +00:00
/// <summary>
/// Record how many inbound packets could not be associated with a simulator circuit.
/// </summary>
2016-06-13 23:21:47 +00:00
public int IncomingOrphanedPacketCount { get ; protected set ; }
2013-08-14 21:33:12 +00:00
2014-11-05 19:02:26 +00:00
/// <summary>
/// Queue some low priority but potentially high volume async requests so that they don't overwhelm available
/// threadpool threads.
/// </summary>
2017-06-13 18:04:18 +00:00
// public JobEngine IpahEngine { get; protected set; }
2014-11-05 19:02:26 +00:00
2014-08-18 23:11:04 +00:00
/// <summary>
2015-01-12 20:56:37 +00:00
/// Run queue empty processing within a single persistent thread.
2014-08-18 23:11:04 +00:00
/// </summary>
2015-01-12 20:56:37 +00:00
/// <remarks>
/// This is the alternative to having every
/// connection schedule its own job in the threadpool which causes performance problems when there are many
/// connections.
/// </remarks>
2016-06-13 23:21:47 +00:00
public JobEngine OqrEngine { get ; protected set ; }
2014-08-18 23:11:04 +00:00
2012-10-16 22:35:05 +00:00
public LLUDPServer (
2016-12-13 19:47:26 +00:00
IPAddress listenIP , ref uint port , int proxyPortOffsetParm ,
2012-10-16 22:35:05 +00:00
IConfigSource configSource , AgentCircuitManager circuitManager )
2009-10-09 08:53:06 +00:00
: base ( listenIP , ( int ) port )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
#region Environment.TickCount Measurement
2017-01-05 19:07:37 +00:00
// Update the port with the one we actually got
port = ( uint ) Port ;
2016-12-13 19:47:26 +00:00
2009-10-06 00:38:14 +00:00
// Measure the resolution of Environment.TickCount
2009-10-21 07:18:35 +00:00
TickCountResolution = 0f ;
2014-08-14 19:41:36 +00:00
for ( int i = 0 ; i < 10 ; i + + )
2009-10-06 00:38:14 +00:00
{
int start = Environment . TickCount ;
int now = start ;
while ( now = = start )
now = Environment . TickCount ;
2016-11-09 11:21:46 +00:00
TickCountResolution + = ( float ) ( now - start ) ;
2009-10-06 00:38:14 +00:00
}
2016-11-09 11:21:46 +00:00
m_log . Info ( "[LLUDPSERVER]: Average Environment.TickCount resolution: " + TickCountResolution * 0.1f + "ms" ) ;
TickCountResolution = 0f ;
for ( int i = 0 ; i < 100 ; i + + )
{
double start = Util . GetTimeStampMS ( ) ;
double now = start ;
while ( now = = start )
now = Util . GetTimeStampMS ( ) ;
TickCountResolution + = ( float ) ( ( now - start ) ) ;
}
TickCountResolution = ( float ) Math . Round ( TickCountResolution * 0.01f , 6 , MidpointRounding . AwayFromZero ) ;
m_log . Info ( "[LLUDPSERVER]: Average Util.GetTimeStampMS resolution: " + TickCountResolution + "ms" ) ;
2009-10-06 00:38:14 +00:00
#endregion Environment.TickCount Measurement
m_circuitManager = circuitManager ;
2009-10-14 18:52:48 +00:00
int sceneThrottleBps = 0 ;
2018-11-25 18:38:37 +00:00
// bool usePools = false;
2009-10-06 00:38:14 +00:00
2009-10-14 18:43:31 +00:00
IConfig config = configSource . Configs [ "ClientStack.LindenUDP" ] ;
if ( config ! = null )
{
m_recvBufferSize = config . GetInt ( "client_socket_rcvbuf_size" , 0 ) ;
2009-10-14 18:52:48 +00:00
sceneThrottleBps = config . GetInt ( "scene_throttle_max_bps" , 0 ) ;
2009-10-23 17:35:47 +00:00
TextureSendLimit = config . GetInt ( "TextureSendLimit" , 20 ) ;
2009-10-30 22:07:56 +00:00
m_defaultRTO = config . GetInt ( "DefaultRTO" , 0 ) ;
m_maxRTO = config . GetInt ( "MaxRTO" , 0 ) ;
2010-08-08 00:57:02 +00:00
m_disableFacelights = config . GetBoolean ( "DisableFacelights" , false ) ;
2012-05-31 23:39:26 +00:00
m_ackTimeout = 1000 * config . GetInt ( "AckTimeout" , 60 ) ;
m_pausedAckTimeout = 1000 * config . GetInt ( "PausedAckTimeout" , 300 ) ;
2009-10-23 17:35:47 +00:00
}
else
{
TextureSendLimit = 20 ;
2012-05-31 23:39:26 +00:00
m_ackTimeout = 1000 * 60 ; // 1 minute
m_pausedAckTimeout = 1000 * 300 ; // 5 minutes
2009-10-14 18:43:31 +00:00
}
2012-10-05 00:43:29 +00:00
// FIXME: This actually only needs to be done once since the PacketPool is shared across all servers.
// However, there is no harm in temporarily doing it multiple times.
IConfig packetConfig = configSource . Configs [ "PacketPool" ] ;
if ( packetConfig ! = null )
{
PacketPool . Instance . RecyclePackets = packetConfig . GetBoolean ( "RecyclePackets" , true ) ;
2018-11-25 18:38:37 +00:00
// PacketPool.Instance.RecycleDataBlocks = packetConfig.GetBoolean("RecycleDataBlocks", true);
// usePools = packetConfig.GetBoolean("RecycleBaseUDPPackets", usePools);
2012-10-05 00:43:29 +00:00
}
2009-11-05 20:01:40 +00:00
#region BinaryStats
config = configSource . Configs [ "Statistics.Binary" ] ;
m_shouldCollectStats = false ;
if ( config ! = null )
{
2013-03-15 20:59:34 +00:00
m_shouldCollectStats = config . GetBoolean ( "Enabled" , false ) ;
binStatsMaxFilesize = TimeSpan . FromSeconds ( config . GetInt ( "packet_headers_period_seconds" , 300 ) ) ;
binStatsDir = config . GetString ( "stats_dir" , "." ) ;
m_aggregatedBWStats = config . GetBoolean ( "aggregatedBWStats" , false ) ;
}
#endregion BinaryStats
2009-11-05 20:01:40 +00:00
2015-09-02 18:54:53 +00:00
Throttle = new TokenBucket ( null , sceneThrottleBps , sceneThrottleBps * 10e-3f ) ;
2011-01-21 00:38:16 +00:00
ThrottleRates = new ThrottleRates ( configSource ) ;
2012-10-17 20:08:15 +00:00
2014-08-16 12:43:26 +00:00
Random rnd = new Random ( Util . EnvironmentTickCount ( ) ) ;
2015-12-09 22:32:48 +00:00
// if (usePools)
// EnablePools();
2008-05-02 19:21:33 +00:00
}
2009-10-14 21:25:58 +00:00
public void Start ( )
2008-06-20 01:35:54 +00:00
{
2012-10-16 20:55:00 +00:00
StartInbound ( ) ;
StartOutbound ( ) ;
2017-06-13 18:04:18 +00:00
// IpahEngine.Start();
2014-09-04 17:24:59 +00:00
OqrEngine . Start ( ) ;
2009-10-06 00:38:14 +00:00
2012-10-16 20:55:00 +00:00
m_elapsedMSSinceLastStatReport = Environment . TickCount ;
}
2014-10-02 22:18:21 +00:00
public void StartInbound ( )
2012-10-16 20:55:00 +00:00
{
2012-06-07 01:44:13 +00:00
m_log . InfoFormat (
2017-06-13 07:56:21 +00:00
"[LLUDPSERVER]: Starting inbound packet processing for the LLUDP server" ) ;
2009-10-14 23:48:27 +00:00
2017-06-13 07:56:21 +00:00
base . StartInbound ( m_recvBufferSize ) ;
2009-10-06 00:38:14 +00:00
2012-10-16 20:55:00 +00:00
// This thread will process the packets received that are placed on the packetInbox
2014-11-25 23:56:32 +00:00
WorkManager . StartThread (
2012-06-07 01:44:13 +00:00
IncomingPacketHandler ,
2014-08-18 21:51:36 +00:00
string . Format ( "Incoming Packets ({0})" , Scene . Name ) ,
2012-06-07 01:44:13 +00:00
ThreadPriority . Normal ,
2018-11-06 15:18:36 +00:00
true ,
2012-06-07 01:44:13 +00:00
true ,
2018-12-02 00:41:01 +00:00
null ,
2012-07-03 23:15:03 +00:00
Watchdog . DEFAULT_WATCHDOG_TIMEOUT_MS ) ;
2012-10-16 20:55:00 +00:00
}
2012-06-07 01:44:13 +00:00
2014-10-02 22:18:21 +00:00
public override void StartOutbound ( )
2012-10-16 20:55:00 +00:00
{
m_log . Info ( "[LLUDPSERVER]: Starting outbound packet processing for the LLUDP server" ) ;
base . StartOutbound ( ) ;
2014-11-25 23:56:32 +00:00
WorkManager . StartThread (
2012-06-07 01:44:13 +00:00
OutgoingPacketHandler ,
2014-08-18 23:11:04 +00:00
string . Format ( "Outgoing Packets ({0})" , Scene . Name ) ,
2012-06-07 01:44:13 +00:00
ThreadPriority . Normal ,
2018-11-06 15:18:36 +00:00
true ,
2012-06-07 01:44:13 +00:00
true ,
2018-12-02 00:41:01 +00:00
null ,
2012-07-03 23:15:03 +00:00
Watchdog . DEFAULT_WATCHDOG_TIMEOUT_MS ) ;
2012-10-16 20:55:00 +00:00
}
2012-02-24 05:02:33 +00:00
2012-10-16 22:35:05 +00:00
public void Stop ( )
2012-10-16 20:55:00 +00:00
{
2014-08-18 23:11:04 +00:00
m_log . Info ( "[LLUDPSERVER]: Shutting down the LLUDP server for " + Scene . Name ) ;
2012-10-16 20:55:00 +00:00
base . StopOutbound ( ) ;
base . StopInbound ( ) ;
2017-06-13 18:04:18 +00:00
// IpahEngine.Stop();
2014-09-04 17:24:59 +00:00
OqrEngine . Stop ( ) ;
2008-06-20 01:35:54 +00:00
}
2018-12-02 00:41:01 +00:00
/// <summary>
2012-06-07 01:44:13 +00:00
/// If the outgoing UDP thread times out, then return client that was being processed to help with debugging.
/// </summary>
/// <returns></returns>
2009-10-06 00:38:14 +00:00
public void AddScene ( IScene scene )
{
2014-08-18 21:51:36 +00:00
if ( Scene ! = null )
2008-11-03 19:02:44 +00:00
{
2009-10-19 22:19:09 +00:00
m_log . Error ( "[LLUDPSERVER]: AddScene() called on an LLUDPServer that already has a scene" ) ;
return ;
2009-10-06 00:38:14 +00:00
}
2009-10-19 22:19:09 +00:00
if ( ! ( scene is Scene ) )
2009-10-06 00:38:14 +00:00
{
2009-10-19 22:19:09 +00:00
m_log . Error ( "[LLUDPSERVER]: AddScene() called with an unrecognized scene type " + scene . GetType ( ) ) ;
return ;
2009-10-06 00:38:14 +00:00
}
2009-10-19 22:19:09 +00:00
2014-08-18 21:51:36 +00:00
Scene = ( Scene ) scene ;
m_location = new Location ( Scene . RegionInfo . RegionHandle ) ;
2017-06-13 18:04:18 +00:00
/ *
2017-01-05 19:07:37 +00:00
IpahEngine
2015-01-12 20:56:37 +00:00
= new JobEngine (
2017-01-05 19:07:37 +00:00
string . Format ( "Incoming Packet Async Handling Engine ({0})" , Scene . Name ) ,
2015-01-12 20:56:37 +00:00
"INCOMING PACKET ASYNC HANDLING ENGINE" ) ;
2017-06-13 18:04:18 +00:00
* /
2019-02-25 21:46:23 +00:00
OqrEngine = new JobEngine (
2017-01-05 19:07:37 +00:00
string . Format ( "Outgoing Queue Refill Engine ({0})" , Scene . Name ) ,
2015-01-12 20:56:37 +00:00
"OUTGOING QUEUE REFILL ENGINE" ) ;
2012-11-15 02:02:59 +00:00
2013-07-22 23:31:57 +00:00
StatsManager . RegisterStat (
new Stat (
"InboxPacketsCount" ,
"Number of LL protocol packets waiting for the second stage of processing after initial receive." ,
"Number of LL protocol packets waiting for the second stage of processing after initial receive." ,
"" ,
"clientstack" ,
scene . Name ,
StatType . Pull ,
MeasuresOfInterest . AverageChangeOverTime ,
2018-01-22 19:50:07 +00:00
stat = > { try { stat . Value = packetInbox . Count ; } catch { } } ,
2013-07-22 23:31:57 +00:00
StatVerbosity . Debug ) ) ;
2012-11-15 02:02:59 +00:00
// XXX: These stats are also pool stats but we register them separately since they are currently not
// turned on and off by EnablePools()/DisablePools()
StatsManager . RegisterStat (
new PercentageStat (
"PacketsReused" ,
"Packets reused" ,
"Number of packets reused out of all requests to the packet pool" ,
"clientstack" ,
2014-08-18 21:51:36 +00:00
Scene . Name ,
2012-11-15 02:02:59 +00:00
StatType . Pull ,
2017-01-05 19:07:37 +00:00
stat = >
{ PercentageStat pstat = ( PercentageStat ) stat ;
pstat . Consequent = PacketPool . Instance . PacketsRequested ;
2012-11-15 02:02:59 +00:00
pstat . Antecedent = PacketPool . Instance . PacketsReused ; } ,
StatVerbosity . Debug ) ) ;
StatsManager . RegisterStat (
new PercentageStat (
"PacketDataBlocksReused" ,
"Packet data blocks reused" ,
"Number of data blocks reused out of all requests to the packet pool" ,
"clientstack" ,
2014-08-18 21:51:36 +00:00
Scene . Name ,
2012-11-15 02:02:59 +00:00
StatType . Pull ,
stat = >
2017-01-05 19:07:37 +00:00
{ PercentageStat pstat = ( PercentageStat ) stat ;
pstat . Consequent = PacketPool . Instance . BlocksRequested ;
2012-11-15 02:02:59 +00:00
pstat . Antecedent = PacketPool . Instance . BlocksReused ; } ,
StatVerbosity . Debug ) ) ;
StatsManager . RegisterStat (
new Stat (
"PacketsPoolCount" ,
"Objects within the packet pool" ,
"The number of objects currently stored within the packet pool" ,
"" ,
"clientstack" ,
2014-08-18 21:51:36 +00:00
Scene . Name ,
2012-11-15 02:02:59 +00:00
StatType . Pull ,
stat = > stat . Value = PacketPool . Instance . PacketsPooled ,
StatVerbosity . Debug ) ) ;
StatsManager . RegisterStat (
new Stat (
"PacketDataBlocksPoolCount" ,
"Objects within the packet data block pool" ,
"The number of objects currently stored within the packet data block pool" ,
"" ,
"clientstack" ,
2014-08-18 21:51:36 +00:00
Scene . Name ,
2012-11-15 02:02:59 +00:00
StatType . Pull ,
stat = > stat . Value = PacketPool . Instance . BlocksPooled ,
StatVerbosity . Debug ) ) ;
2014-10-02 22:49:37 +00:00
StatsManager . RegisterStat (
new Stat (
"OutgoingPacketsQueuedCount" ,
"Packets queued for outgoing send" ,
"Number of queued outgoing packets across all connections" ,
"" ,
"clientstack" ,
Scene . Name ,
StatType . Pull ,
MeasuresOfInterest . AverageChangeOverTime ,
stat = > stat . Value = GetTotalQueuedOutgoingPackets ( ) ,
StatVerbosity . Info ) ) ;
2017-06-13 18:04:18 +00:00
/ *
2015-01-12 20:56:37 +00:00
StatsManager . RegisterStat (
new Stat (
"IncomingPacketAsyncRequestsWaiting" ,
"Number of incoming packets waiting for async processing in engine." ,
"" ,
"" ,
"clientstack" ,
Scene . Name ,
StatType . Pull ,
MeasuresOfInterest . None ,
stat = > stat . Value = IpahEngine . JobsWaiting ,
StatVerbosity . Debug ) ) ;
2017-06-13 18:04:18 +00:00
* /
2015-01-12 20:56:37 +00:00
StatsManager . RegisterStat (
new Stat (
"OQRERequestsWaiting" ,
"Number of outgong queue refill requests waiting for processing." ,
"" ,
"" ,
"clientstack" ,
Scene . Name ,
StatType . Pull ,
MeasuresOfInterest . None ,
stat = > stat . Value = OqrEngine . JobsWaiting ,
StatVerbosity . Debug ) ) ;
2017-01-05 19:07:37 +00:00
2019-02-25 21:46:23 +00:00
StatsManager . RegisterStat (
new Stat (
"UDPBuffersPoolCount" ,
"Buffers in the UDP buffers pool" ,
"The number of buffers currently stored within the UDP buffers pool" ,
"" ,
"clientstack" ,
Scene . Name ,
StatType . Pull ,
2019-02-26 00:43:44 +00:00
stat = > stat . Value = m_udpBuffersPoolPtr + 1 ,
2019-02-25 21:46:23 +00:00
StatVerbosity . Debug ) ) ;
2015-09-02 18:54:53 +00:00
2014-10-02 22:18:21 +00:00
LLUDPServerCommands commands = new LLUDPServerCommands ( MainConsole . Instance , this ) ;
commands . Register ( ) ;
2008-05-02 19:21:33 +00:00
}
2009-10-06 00:38:14 +00:00
public bool HandlesRegion ( Location x )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
return x = = m_location ;
2008-05-02 19:21:33 +00:00
}
2014-10-02 22:49:37 +00:00
public int GetTotalQueuedOutgoingPackets ( )
{
int total = 0 ;
foreach ( ScenePresence sp in Scene . GetScenePresences ( ) )
{
2015-01-13 19:27:29 +00:00
// XXX: Need a better way to determine which IClientAPIs have UDPClients (NPCs do not, for instance).
if ( sp . ControllingClient is LLClientView )
{
LLUDPClient udpClient = ( ( LLClientView ) sp . ControllingClient ) . UDPClient ;
total + = udpClient . GetTotalPacketsQueuedCount ( ) ;
}
2014-10-02 22:49:37 +00:00
}
return total ;
}
2013-10-23 23:37:49 +00:00
// public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
// {
// // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way
// if ((packet.Type == PacketType.CoarseLocationUpdate || packet.Type == PacketType.AvatarGroupsReply) && allowSplitting)
// allowSplitting = false;
/ /
// if (allowSplitting && packet.HasVariableBlocks)
// {
// byte[][] datas = packet.ToBytesMultiple();
// int packetCount = datas.Length;
/ /
// if (packetCount < 1)
// m_log.Error("[LLUDPSERVER]: Failed to split " + packet.Type + " with estimated length " + packet.Length);
/ /
// for (int i = 0; i < packetCount; i++)
// {
// byte[] data = datas[i];
// m_scene.ForEachClient(
// delegate(IClientAPI client)
// {
// if (client is LLClientView)
// SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
// }
// );
// }
// }
// else
// {
// byte[] data = packet.ToBytes();
// m_scene.ForEachClient(
// delegate(IClientAPI client)
// {
// if (client is LLClientView)
// SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category, null);
// }
// );
// }
// }
2009-10-06 00:38:14 +00:00
2010-12-01 22:23:42 +00:00
/// <summary>
/// Start the process of sending a packet to the client.
/// </summary>
/// <param name="udpClient"></param>
/// <param name="packet"></param>
/// <param name="category"></param>
/// <param name="allowSplitting"></param>
2011-10-12 18:22:30 +00:00
/// <param name="method">
/// The method to call if the packet is not acked by the client. If null, then a standard
/// resend of the packet is done.
/// </param>
2011-12-08 21:45:02 +00:00
public virtual void SendPacket (
2011-10-12 18:22:30 +00:00
LLUDPClient udpClient , Packet packet , ThrottleOutPacketType category , bool allowSplitting , UnackedPacketMethod method )
2008-08-09 05:26:33 +00:00
{
2009-10-06 09:38:00 +00:00
// CoarseLocationUpdate packets cannot be split in an automated way
2018-12-30 02:41:13 +00:00
if ( allowSplitting & & packet . HasVariableBlocks & & packet . Type ! = PacketType . CoarseLocationUpdate & &
packet . Length + 20 > MTU )
2008-10-31 19:41:07 +00:00
{
2009-10-06 00:38:14 +00:00
byte [ ] [ ] datas = packet . ToBytesMultiple ( ) ;
int packetCount = datas . Length ;
2009-10-30 07:08:41 +00:00
if ( packetCount < 1 )
m_log . Error ( "[LLUDPSERVER]: Failed to split " + packet . Type + " with estimated length " + packet . Length ) ;
2009-10-06 00:38:14 +00:00
for ( int i = 0 ; i < packetCount ; i + + )
2019-01-20 20:58:27 +00:00
SendPacketData ( udpClient , datas [ i ] , packet . Type , category , method ) ;
2009-10-06 00:38:14 +00:00
}
else
{
byte [ ] data = packet . ToBytes ( ) ;
2018-01-20 12:11:07 +00:00
SendPacketData ( udpClient , data , packet . Type , category , method ) ;
2009-10-06 00:38:14 +00:00
}
2012-10-11 22:58:37 +00:00
PacketPool . Instance . ReturnPacket ( packet ) ;
2009-10-06 00:38:14 +00:00
}
2009-03-25 19:30:36 +00:00
2018-12-29 23:03:03 +00:00
public static int ZeroEncode ( byte [ ] src , int srclen , byte [ ] dest )
{
Buffer . BlockCopy ( src , 0 , dest , 0 , 6 ) ;
int zerolen = 6 ;
byte zerocount = 0 ;
for ( int i = zerolen ; i < srclen ; i + + )
{
if ( src [ i ] = = 0x00 )
{
zerocount + + ;
if ( zerocount = = 0 )
{
dest [ zerolen + + ] = 0x00 ;
dest [ zerolen + + ] = 0xff ;
zerocount + + ;
}
}
else
{
if ( zerocount ! = 0 )
{
dest [ zerolen + + ] = 0x00 ;
2019-01-20 20:58:27 +00:00
dest [ zerolen + + ] = zerocount ;
2018-12-29 23:03:03 +00:00
zerocount = 0 ;
}
dest [ zerolen + + ] = src [ i ] ;
}
}
if ( zerocount ! = 0 )
{
dest [ zerolen + + ] = 0x00 ;
2019-01-20 20:58:27 +00:00
dest [ zerolen + + ] = zerocount ;
2018-12-29 23:03:03 +00:00
}
2019-01-20 20:58:27 +00:00
return zerolen ;
2018-12-29 23:03:03 +00:00
}
2010-12-01 22:23:42 +00:00
/// <summary>
/// Start the process of sending a packet to the client.
/// </summary>
/// <param name="udpClient"></param>
/// <param name="data"></param>
/// <param name="type"></param>
2011-10-12 18:22:30 +00:00
/// <param name="category"></param>
/// <param name="method">
/// The method to call if the packet is not acked by the client. If null, then a standard
/// resend of the packet is done.
/// </param>
2013-10-23 23:33:14 +00:00
/// <returns>true if the data was sent immediately, false if it was queued for sending</returns>
public bool SendPacketData (
2011-10-12 18:22:30 +00:00
LLUDPClient udpClient , byte [ ] data , PacketType type , ThrottleOutPacketType category , UnackedPacketMethod method )
2009-10-06 00:38:14 +00:00
{
2009-10-13 17:38:35 +00:00
int dataLength = data . Length ;
bool doZerocode = ( data [ 0 ] & Helpers . MSG_ZEROCODED ) ! = 0 ;
2009-10-16 21:26:58 +00:00
bool doCopy = true ;
2009-10-13 17:38:35 +00:00
2009-10-06 00:38:14 +00:00
// Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum.
// The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting
// there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here
// to accomodate for both common scenarios and provide ample room for ACK appending in both
2019-02-25 21:46:23 +00:00
//int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200;
2009-10-06 00:38:14 +00:00
2019-02-25 21:46:23 +00:00
//UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize);
UDPPacketBuffer buffer = GetNewUDPBuffer ( udpClient . RemoteEndPoint ) ;
2008-10-31 19:41:07 +00:00
2009-10-06 00:38:14 +00:00
// Zerocode if needed
if ( doZerocode )
{
2009-10-16 21:26:58 +00:00
try
{
2018-12-29 23:03:03 +00:00
int testlen = ZeroEncode ( data , dataLength , buffer . Data ) ;
if ( testlen < = dataLength )
{
dataLength = testlen ;
doCopy = false ;
}
else
data [ 0 ] = ( byte ) ( data [ 0 ] & ~ Helpers . MSG_ZEROCODED ) ;
2009-10-16 21:26:58 +00:00
}
2009-10-06 00:38:14 +00:00
catch ( IndexOutOfRangeException )
{
// The packet grew larger than the bufferSize while zerocoding.
// Remove the MSG_ZEROCODED flag and send the unencoded data
// instead
2009-10-16 20:22:45 +00:00
m_log . Debug ( "[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength +
" and BufferLength=" + buffer . Data . Length + ". Removing MSG_ZEROCODED flag" ) ;
2009-10-06 00:38:14 +00:00
data [ 0 ] = ( byte ) ( data [ 0 ] & ~ Helpers . MSG_ZEROCODED ) ;
2009-03-25 19:30:36 +00:00
}
2009-10-06 00:38:14 +00:00
}
2009-10-16 21:26:58 +00:00
// If the packet data wasn't already copied during zerocoding, copy it now
if ( doCopy )
2009-10-06 00:38:14 +00:00
{
2019-02-25 21:46:23 +00:00
//if (dataLength <= buffer.Data.Length)
if ( dataLength < = LLUDPServer . MTU )
2009-10-16 21:26:58 +00:00
{
Buffer . BlockCopy ( data , 0 , buffer . Data , 0 , dataLength ) ;
}
else
{
2015-09-22 17:39:59 +00:00
m_log . Error ( "[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" +
type + ", DataLength=" + dataLength + ", BufferLength=" + buffer . Data . Length ) ;
2019-02-25 21:46:23 +00:00
// buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, dataLength);
buffer = GetNewUDPBuffer ( udpClient . RemoteEndPoint ) ;
2009-11-04 23:12:56 +00:00
Buffer . BlockCopy ( data , 0 , b uffer . Data , 0 , dataLength ) ;
2009-10-16 21:26:58 +00:00
}
2009-10-06 00:38:14 +00:00
}
2018-12-29 23:03:03 +00:00
data = null ;
2009-10-06 00:38:14 +00:00
buffer . DataLength = dataLength ;
#region Queue or Send
2013-01-16 18:29:27 +00:00
bool highPriority = false ;
if ( category ! = ThrottleOutPacketType . Unknown & & ( category & ThrottleOutPacketType . HighPriority ) ! = 0 )
{
category = ( ThrottleOutPacketType ) ( ( int ) category & 127 ) ;
highPriority = true ;
}
2011-04-18 23:48:49 +00:00
OutgoingPacket outgoingPacket = new OutgoingPacket ( udpClient , buffer , category , null ) ;
2014-08-13 21:57:14 +00:00
2011-04-18 23:48:49 +00:00
// If we were not provided a method for handling unacked, use the UDPServer default method
2014-08-13 21:57:14 +00:00
if ( ( outgoingPacket . Buffer . Data [ 0 ] & Helpers . MSG_RELIABLE ) ! = 0 )
outgoingPacket . UnackedMethod = ( ( method = = null ) ? delegate ( OutgoingPacket oPacket ) { ResendUnacked ( oPacket ) ; } : method ) ;
2009-10-06 00:38:14 +00:00
2017-01-05 19:07:37 +00:00
// If a Linden Lab 1.23.5 client receives an update packet after a kill packet for an object, it will
2010-12-02 01:55:49 +00:00
// continue to display the deleted object until relog. Therefore, we need to always queue a kill object
// packet so that it isn't sent before a queued update packet.
bool requestQueue = type = = PacketType . KillObject ;
2013-01-16 18:29:27 +00:00
if ( ! outgoingPacket . Client . EnqueueOutgoing ( outgoingPacket , requestQueue , highPriority ) )
2013-10-23 23:33:14 +00:00
{
2009-10-06 00:38:14 +00:00
SendPacketFinal ( outgoingPacket ) ;
2013-10-23 23:33:14 +00:00
return true ;
}
2013-11-08 17:55:01 +00:00
return false ;
2009-10-06 00:38:14 +00:00
#endregion Queue or Send
}
2019-03-05 16:01:29 +00:00
public unsafe UDPPacketBuffer ZeroEncode ( UDPPacketBuffer input )
{
UDPPacketBuffer zb = GetNewUDPBuffer ( null ) ;
int srclen = input . DataLength ;
byte [ ] src = input . Data ;
byte [ ] dest = zb . Data ;
int zerolen = 6 ;
byte zerocount = 0 ;
for ( int i = zerolen ; i < srclen ; i + + )
{
if ( src [ i ] = = 0x00 )
{
zerocount + + ;
if ( zerocount = = 0 )
{
dest [ zerolen + + ] = 0x00 ;
dest [ zerolen + + ] = 0xff ;
zerocount + + ;
}
}
else
{
if ( zerocount ! = 0 )
{
dest [ zerolen + + ] = 0x00 ;
dest [ zerolen + + ] = zerocount ;
zerocount = 0 ;
}
dest [ zerolen + + ] = src [ i ] ;
}
}
if ( zerocount ! = 0 )
{
dest [ zerolen + + ] = 0x00 ;
dest [ zerolen + + ] = zerocount ;
}
if ( zerolen > = srclen )
{
FreeUDPBuffer ( zb ) ;
src [ 0 ] & = unchecked ( ( byte ) ~ Helpers . MSG_ZEROCODED ) ;
return input ;
}
Buffer . BlockCopy ( src , 0 , dest , 0 , 6 ) ;
zb . RemoteEndPoint = input . RemoteEndPoint ;
zb . DataLength = zerolen ;
FreeUDPBuffer ( input ) ;
return zb ;
}
2019-02-27 10:07:25 +00:00
public void SendUDPPacket (
2019-03-05 16:01:29 +00:00
LLUDPClient udpClient , UDPPacketBuffer buffer , ThrottleOutPacketType category , UnackedPacketMethod method , bool forcequeue , bool zerocode )
2019-02-27 10:07:25 +00:00
{
bool highPriority = false ;
2019-03-05 16:01:29 +00:00
if ( zerocode )
buffer = ZeroEncode ( buffer ) ;
2019-02-27 10:07:25 +00:00
if ( category ! = ThrottleOutPacketType . Unknown & & ( category & ThrottleOutPacketType . HighPriority ) ! = 0 )
{
category = ( ThrottleOutPacketType ) ( ( int ) category & 127 ) ;
highPriority = true ;
}
OutgoingPacket outgoingPacket = new OutgoingPacket ( udpClient , buffer , category , null ) ;
// If we were not provided a method for handling unacked, use the UDPServer default method
if ( ( outgoingPacket . Buffer . Data [ 0 ] & Helpers . MSG_RELIABLE ) ! = 0 )
outgoingPacket . UnackedMethod = ( ( method = = null ) ? delegate ( OutgoingPacket oPacket ) { ResendUnacked ( oPacket ) ; } : method ) ;
if ( ! outgoingPacket . Client . EnqueueOutgoing ( outgoingPacket , forcequeue , highPriority ) )
SendPacketFinal ( outgoingPacket ) ;
}
2009-10-13 18:14:45 +00:00
public void SendAcks ( LLUDPClient udpClient )
2009-10-06 00:38:14 +00:00
{
uint ack ;
2009-10-13 18:14:45 +00:00
if ( udpClient . PendingAcks . Dequeue ( out ack ) )
2009-10-06 00:38:14 +00:00
{
List < PacketAckPacket . PacketsBlock > blocks = new List < PacketAckPacket . PacketsBlock > ( ) ;
PacketAckPacket . PacketsBlock block = new PacketAckPacket . PacketsBlock ( ) ;
block . ID = ack ;
blocks . Add ( block ) ;
2009-10-13 18:14:45 +00:00
while ( udpClient . PendingAcks . Dequeue ( out ack ) )
2009-06-25 07:50:02 +00:00
{
2009-10-06 00:38:14 +00:00
block = new PacketAckPacket . PacketsBlock ( ) ;
block . ID = ack ;
blocks . Add ( block ) ;
2009-06-25 07:50:02 +00:00
}
2009-10-06 00:38:14 +00:00
PacketAckPacket packet = new PacketAckPacket ( ) ;
packet . Header . Reliable = false ;
packet . Packets = blocks . ToArray ( ) ;
2011-04-18 23:48:49 +00:00
SendPacket ( udpClient , packet , ThrottleOutPacketType . Unknown , true , null ) ;
2009-03-25 19:30:36 +00:00
}
2009-09-30 16:00:09 +00:00
}
2008-10-31 19:41:07 +00:00
2009-10-13 18:14:45 +00:00
public void SendPing ( LLUDPClient udpClient )
2009-10-06 09:38:00 +00:00
{
2009-10-13 18:14:45 +00:00
StartPingCheckPacket pc = ( StartPingCheckPacket ) PacketPool . Instance . GetPacket ( PacketType . StartPingCheck ) ;
pc . PingID . PingID = ( byte ) udpClient . CurrentPingSequence + + ;
2009-10-21 18:59:48 +00:00
// We *could* get OldestUnacked, but it would hurt performance and not provide any benefit
pc . PingID . OldestUnacked = 0 ;
2009-10-13 18:14:45 +00:00
2011-04-18 23:48:49 +00:00
SendPacket ( udpClient , pc , ThrottleOutPacketType . Unknown , false , null ) ;
2014-08-14 19:41:36 +00:00
udpClient . m_lastStartpingTimeMS = Util . EnvironmentTickCount ( ) ;
2009-10-06 09:38:00 +00:00
}
2009-10-30 07:43:46 +00:00
public void CompletePing ( LLUDPClient udpClient , byte pingID )
{
CompletePingCheckPacket completePing = new CompletePingCheckPacket ( ) ;
completePing . PingID . PingID = pingID ;
2011-04-18 23:48:49 +00:00
SendPacket ( udpClient , completePing , ThrottleOutPacketType . Unknown , false , null ) ;
2009-10-30 07:43:46 +00:00
}
2012-06-08 02:53:03 +00:00
public void HandleUnacked ( LLClientView client )
2008-10-31 19:41:07 +00:00
{
2012-06-08 02:53:03 +00:00
LLUDPClient udpClient = client . UDPClient ;
2019-02-26 15:02:57 +00:00
if ( ! client . IsActive | | ! udpClient . IsConnected )
2009-10-20 01:50:31 +00:00
return ;
// Disconnect an agent if no packets are received for some time
2012-05-31 23:39:26 +00:00
int timeoutTicks = m_ackTimeout ;
// Allow more slack if the client is "paused" eg file upload dialogue is open
// Some sort of limit is needed in case the client crashes, loses its network connection
// or some other disaster prevents it from sendung the AgentResume
if ( udpClient . IsPaused )
timeoutTicks = m_pausedAckTimeout ;
2012-06-12 01:16:36 +00:00
if ( client . IsActive & &
2013-07-29 22:38:54 +00:00
( Environment . TickCount & Int32 . MaxValue ) - udpClient . TickLastPacketReceived > timeoutTicks )
2008-10-31 19:41:07 +00:00
{
2012-06-12 01:16:36 +00:00
// We must set IsActive synchronously so that we can stop the packet loop reinvoking this method, even
// though it's set later on by LLClientView.Close()
client . IsActive = false ;
2012-06-12 01:03:31 +00:00
2012-06-12 01:16:36 +00:00
// Fire this out on a different thread so that we don't hold up outgoing packet processing for
// everybody else if this is being called due to an ack timeout.
// This is the same as processing as the async process of a logout request.
2014-11-04 00:55:48 +00:00
Util . FireAndForget (
o = > DeactivateClientDueToTimeout ( client , timeoutTicks ) , null , "LLUDPServer.DeactivateClientDueToTimeout" ) ;
2012-06-08 02:53:03 +00:00
2009-10-20 01:50:31 +00:00
return ;
}
2009-10-14 01:56:54 +00:00
2009-10-21 18:59:48 +00:00
// Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO
List < OutgoingPacket > expiredPackets = udpClient . NeedAcks . GetExpiredPackets ( udpClient . RTO ) ;
if ( expiredPackets ! = null )
2009-10-20 01:50:31 +00:00
{
2011-04-18 23:48:49 +00:00
//m_log.Debug("[LLUDPSERVER]: Handling " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO);
2009-10-25 07:40:21 +00:00
// Exponential backoff of the retransmission timeout
udpClient . BackoffRTO ( ) ;
2011-04-18 23:48:49 +00:00
for ( int i = 0 ; i < expiredPackets . Count ; + + i )
2011-04-21 06:08:51 +00:00
expiredPackets [ i ] . UnackedMethod ( expiredPackets [ i ] ) ;
2011-04-18 23:48:49 +00:00
}
}
2009-10-23 08:33:05 +00:00
2011-04-18 23:48:49 +00:00
public void ResendUnacked ( OutgoingPacket outgoingPacket )
{
//m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
// outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
2009-10-06 00:38:14 +00:00
2011-04-18 23:48:49 +00:00
// Set the resent flag
outgoingPacket . Buffer . Data [ 0 ] = ( byte ) ( outgoingPacket . Buffer . Data [ 0 ] | Helpers . MSG_RESENT ) ;
outgoingPacket . Category = ThrottleOutPacketType . Resend ;
2009-10-06 00:38:14 +00:00
2011-04-18 23:48:49 +00:00
// Bump up the resend count on this packet
Interlocked . Increment ( ref outgoingPacket . ResendCount ) ;
2009-10-06 00:38:14 +00:00
2011-04-18 23:48:49 +00:00
// Requeue or resend the packet
if ( ! outgoingPacket . Client . EnqueueOutgoing ( outgoingPacket , false ) )
SendPacketFinal ( outgoingPacket ) ;
2009-09-30 16:00:09 +00:00
}
2009-10-06 00:38:14 +00:00
2009-10-13 23:53:19 +00:00
public void Flush ( LLUDPClient udpClient )
2009-10-06 00:38:14 +00:00
{
2009-10-06 17:12:59 +00:00
// FIXME: Implement?
}
2010-09-17 00:16:21 +00:00
/// <summary>
/// Actually send a packet to a client.
/// </summary>
/// <param name="outgoingPacket"></param>
2009-10-06 17:12:59 +00:00
internal void SendPacketFinal ( OutgoingPacket outgoingPacket )
{
UDPPacketBuffer buffer = outgoingPacket . Buffer ;
2019-02-26 15:02:57 +00:00
if ( buffer = = null ) // canceled packet
return ;
LLUDPClient udpClient = outgoingPacket . Client ;
if ( ! udpClient . IsConnected )
return ;
2009-10-06 17:12:59 +00:00
byte flags = buffer . Data [ 0 ] ;
bool isResend = ( flags & Helpers . MSG_RESENT ) ! = 0 ;
bool isReliable = ( flags & Helpers . MSG_RELIABLE ) ! = 0 ;
2010-04-02 23:25:14 +00:00
bool isZerocoded = ( flags & Helpers . MSG_ZEROCODED ) ! = 0 ;
2009-10-14 01:56:54 +00:00
2009-10-06 17:12:59 +00:00
int dataLength = buffer . DataLength ;
2010-04-02 23:25:14 +00:00
// NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here
2014-10-31 23:34:43 +00:00
if ( ! isZerocoded & & ! isResend & & outgoingPacket . UnackedMethod = = null )
2009-10-06 17:12:59 +00:00
{
2010-04-02 23:25:14 +00:00
// Keep appending ACKs until there is no room left in the buffer or there are
// no more ACKs to append
2018-12-29 23:03:03 +00:00
int ackCount = 0 ;
2010-04-02 23:25:14 +00:00
uint ack ;
2019-03-10 11:23:38 +00:00
while ( dataLength + 5 < MTU & & ackCount < 256 & & udpClient . PendingAcks . Dequeue ( out ack ) )
2010-04-02 23:25:14 +00:00
{
Utils . UIntToBytesBig ( ack , buffer . Data , dataLength ) ;
dataLength + = 4 ;
+ + ackCount ;
}
2009-10-06 17:12:59 +00:00
2010-04-02 23:25:14 +00:00
if ( ackCount > 0 )
{
// Set the last byte of the packet equal to the number of appended ACKs
buffer . Data [ dataLength + + ] = ( byte ) ackCount ;
// Set the appended ACKs flag on this packet
buffer . Data [ 0 ] = ( byte ) ( buffer . Data [ 0 ] | Helpers . MSG_APPENDED_ACKS ) ;
}
2019-02-25 21:46:23 +00:00
buffer . DataLength = dataLength ;
2009-10-06 17:12:59 +00:00
}
if ( ! isResend )
{
// Not a resend, assign a new sequence number
2009-10-13 18:14:45 +00:00
uint sequenceNumber = ( uint ) Interlocked . Increment ( ref udpClient . CurrentSequence ) ;
2009-10-06 17:12:59 +00:00
Utils . UIntToBytesBig ( sequenceNumber , buffer . Data , 1 ) ;
outgoingPacket . SequenceNumber = sequenceNumber ;
}
2011-02-02 20:00:50 +00:00
else
{
Interlocked . Increment ( ref udpClient . PacketsResent ) ;
2013-10-31 23:59:22 +00:00
PacketsResentCount + + ;
2011-02-02 20:00:50 +00:00
}
2009-10-06 17:12:59 +00:00
// Stats tracking
2009-10-13 18:14:45 +00:00
Interlocked . Increment ( ref udpClient . PacketsSent ) ;
2013-10-31 23:45:52 +00:00
PacketsSentCount + + ;
2018-12-29 23:03:03 +00:00
SyncSend ( buffer ) ;
2019-02-25 21:46:23 +00:00
2018-12-29 23:03:03 +00:00
// Keep track of when this packet was sent out (right now)
outgoingPacket . TickCount = Environment . TickCount & Int32 . MaxValue ;
2019-02-25 21:46:23 +00:00
if ( outgoingPacket . UnackedMethod = = null )
FreeUDPBuffer ( buffer ) ;
else if ( ! isResend )
{
// Add this packet to the list of ACK responses we are waiting on from the server
udpClient . NeedAcks . Add ( outgoingPacket ) ;
}
2014-09-24 22:42:57 +00:00
if ( udpClient . DebugDataOutLevel > 0 )
m_log . DebugFormat (
"[LLUDPSERVER]: Sending packet #{0} (rel: {1}, res: {2}) to {3} from {4}" ,
outgoingPacket . SequenceNumber , isReliable , isResend , udpClient . AgentID , Scene . Name ) ;
2009-10-06 00:38:14 +00:00
}
2016-06-13 23:21:47 +00:00
protected void RecordMalformedInboundPacket ( IPEndPoint endPoint )
2013-08-14 21:07:22 +00:00
{
// if (m_malformedCount < 100)
// m_log.DebugFormat("[LLUDPSERVER]: Dropped malformed packet: " + e.ToString());
IncomingMalformedPacketCount + + ;
if ( ( IncomingMalformedPacketCount % 10000 ) = = 0 )
m_log . WarnFormat (
2017-01-05 19:07:37 +00:00
"[LLUDPSERVER]: Received {0} malformed packets so far, probable network attack. Last was from {1}" ,
2013-08-14 21:07:22 +00:00
IncomingMalformedPacketCount , endPoint ) ;
}
2011-12-08 20:52:34 +00:00
public override void PacketReceived ( UDPPacketBuffer buffer )
2008-10-23 17:16:13 +00:00
{
2009-10-06 00:38:14 +00:00
// Debugging/Profiling
2009-10-13 18:14:45 +00:00
//try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; }
2009-10-06 00:38:14 +00:00
//catch (Exception) { }
2011-12-08 20:52:34 +00:00
// m_log.DebugFormat(
// "[LLUDPSERVER]: Packet received from {0} in {1}", buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
2009-10-06 00:38:14 +00:00
2009-10-13 21:50:03 +00:00
LLUDPClient udpClient = null ;
2009-10-06 00:38:14 +00:00
Packet packet = null ;
int packetEnd = buffer . DataLength - 1 ;
2012-10-16 22:35:05 +00:00
IPEndPoint endPoint = ( IPEndPoint ) buffer . RemoteEndPoint ;
2009-10-06 00:38:14 +00:00
#region Decoding
2011-07-27 06:35:19 +00:00
if ( buffer . DataLength < 7 )
2011-12-08 20:52:34 +00:00
{
// m_log.WarnFormat(
// "[LLUDPSERVER]: Dropping undersized packet with {0} bytes received from {1} in {2}",
// buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
2013-08-14 21:07:22 +00:00
RecordMalformedInboundPacket ( endPoint ) ;
2019-02-25 21:46:23 +00:00
FreeUDPBuffer ( buffer ) ;
2012-10-16 22:35:05 +00:00
return ; // Drop undersized packet
2011-12-08 20:52:34 +00:00
}
2011-07-27 06:35:19 +00:00
int headerLen = 7 ;
if ( buffer . Data [ 6 ] = = 0xFF )
{
if ( buffer . Data [ 7 ] = = 0xFF )
headerLen = 10 ;
else
headerLen = 8 ;
}
if ( buffer . DataLength < headerLen )
2011-12-08 20:52:34 +00:00
{
// m_log.WarnFormat(
// "[LLUDPSERVER]: Dropping packet with malformed header received from {0} in {1}",
// buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName);
2013-08-14 21:07:22 +00:00
RecordMalformedInboundPacket ( endPoint ) ;
2019-02-25 21:46:23 +00:00
FreeUDPBuffer ( buffer ) ;
2011-07-27 06:35:19 +00:00
return ; // Malformed header
2011-12-08 20:52:34 +00:00
}
2011-07-27 06:35:19 +00:00
2008-10-23 17:16:13 +00:00
try
{
2019-02-25 23:30:01 +00:00
// get a buffer for zero decode using the udp buffers pool
UDPPacketBuffer zerodecodebufferholder = null ;
byte [ ] zerodecodebuffer = null ;
// only if needed
if ( ( ( buffer . Data [ 0 ] & Helpers . MSG_ZEROCODED ) ! = 0 ) )
{
zerodecodebufferholder = GetNewUDPBuffer ( null ) ;
zerodecodebuffer = zerodecodebufferholder . Data ;
}
packet = Packet . BuildPacket ( buffer . Data , ref packetEnd , zerodecodebuffer ) ;
2012-10-16 22:35:05 +00:00
// If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we
// assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all
// bytes are copied out).
2019-02-25 23:30:01 +00:00
// packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, zerodecodebuffer);
if ( zerodecodebufferholder ! = null )
FreeUDPBuffer ( zerodecodebufferholder ) ;
2008-10-23 17:16:13 +00:00
}
2012-10-12 00:39:37 +00:00
catch ( Exception e )
2011-07-27 06:35:19 +00:00
{
2013-08-14 21:07:22 +00:00
if ( IncomingMalformedPacketCount < 100 )
2011-07-27 06:35:19 +00:00
m_log . DebugFormat ( "[LLUDPSERVER]: Dropped malformed packet: " + e . ToString ( ) ) ;
}
2009-10-06 00:38:14 +00:00
// Fail-safe check
if ( packet = = null )
{
2013-08-14 21:07:22 +00:00
if ( IncomingMalformedPacketCount < 100 )
{
2013-08-14 21:33:12 +00:00
m_log . WarnFormat ( "[LLUDPSERVER]: Malformed data, cannot parse {0} byte packet from {1}, data {2}:" ,
buffer . DataLength , buffer . RemoteEndPoint , Utils . BytesToHexString ( buffer . Data , buffer . DataLength , null ) ) ;
2013-08-14 21:07:22 +00:00
}
RecordMalformedInboundPacket ( endPoint ) ;
2019-02-25 21:46:23 +00:00
FreeUDPBuffer ( buffer ) ;
2009-10-06 00:38:14 +00:00
return ;
}
2008-10-23 17:16:13 +00:00
2009-10-06 00:38:14 +00:00
#endregion Decoding
2009-10-13 18:28:08 +00:00
#region Packet to Client Mapping
2009-10-06 00:38:14 +00:00
2012-07-15 21:01:58 +00:00
// If there is already a client for this endpoint, don't process UseCircuitCode
IClientAPI client = null ;
2015-09-02 18:54:53 +00:00
if ( ! Scene . TryGetClient ( endPoint , out client ) | | ! ( client is LLClientView ) )
2010-11-08 12:48:35 +00:00
{
2012-07-15 21:01:58 +00:00
// UseCircuitCode handling
if ( packet . Type = = PacketType . UseCircuitCode )
2012-07-15 20:28:47 +00:00
{
2012-07-15 21:01:58 +00:00
// And if there is a UseCircuitCode pending, also drop it
lock ( m_pendingCache )
{
2014-09-14 01:28:42 +00:00
if ( m_pendingCache . Contains ( endPoint ) )
2019-02-25 21:46:23 +00:00
{
FreeUDPBuffer ( buffer ) ;
2012-07-15 21:01:58 +00:00
return ;
2019-02-25 21:46:23 +00:00
}
2009-10-21 20:47:16 +00:00
2014-09-14 01:28:42 +00:00
m_pendingCache . AddOrUpdate ( endPoint , new Queue < UDPPacketBuffer > ( ) , 60 ) ;
2012-07-15 21:01:58 +00:00
}
2009-10-21 20:47:16 +00:00
2019-02-25 21:46:23 +00:00
Util . FireAndForget ( HandleUseCircuitCode , new object [ ] { endPoint , packet } ) ;
FreeUDPBuffer ( buffer ) ;
2012-07-15 21:01:58 +00:00
return ;
}
2008-10-23 17:16:13 +00:00
}
2009-10-06 00:38:14 +00:00
2012-07-15 21:01:58 +00:00
// If this is a pending connection, enqueue, don't process yet
lock ( m_pendingCache )
New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 18:54:35 +00:00
{
2012-07-15 21:01:58 +00:00
Queue < UDPPacketBuffer > queue ;
2012-10-16 23:03:02 +00:00
if ( m_pendingCache . TryGetValue ( endPoint , out queue ) )
2012-07-15 19:45:06 +00:00
{
2012-07-15 21:01:58 +00:00
//m_log.DebugFormat("[LLUDPSERVER]: Enqueued a {0} packet into the pending queue", packet.Type);
queue . Enqueue ( buffer ) ;
return ;
2012-07-15 19:45:06 +00:00
}
New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 18:54:35 +00:00
}
2009-10-06 00:38:14 +00:00
2019-02-25 21:46:23 +00:00
FreeUDPBuffer ( buffer ) ;
2009-10-06 00:38:14 +00:00
// Determine which agent this packet came from
2012-07-15 21:01:58 +00:00
if ( client = = null | | ! ( client is LLClientView ) )
2008-10-23 17:16:13 +00:00
{
2011-01-05 15:14:09 +00:00
//m_log.Debug("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName);
2013-08-14 21:33:12 +00:00
IncomingOrphanedPacketCount + + ;
if ( ( IncomingOrphanedPacketCount % 10000 ) = = 0 )
m_log . WarnFormat (
2017-01-05 19:07:37 +00:00
"[LLUDPSERVER]: Received {0} orphaned packets so far. Last was from {1}" ,
2013-08-14 21:33:12 +00:00
IncomingOrphanedPacketCount , endPoint ) ;
2009-10-06 00:38:14 +00:00
return ;
2009-09-30 16:00:09 +00:00
}
2008-05-16 01:22:11 +00:00
2009-10-13 21:50:03 +00:00
udpClient = ( ( LLClientView ) client ) . UDPClient ;
2009-10-14 01:56:54 +00:00
if ( ! udpClient . IsConnected )
2012-06-22 17:04:12 +00:00
{
2015-09-02 18:54:53 +00:00
m_log . Debug ( "[LLUDPSERVER]: Received a " + packet . Type + " packet for a unConnected client in " + Scene . RegionInfo . RegionName ) ;
2009-10-14 01:56:54 +00:00
return ;
2012-06-22 17:04:12 +00:00
}
2009-10-14 01:56:54 +00:00
2009-10-13 18:28:08 +00:00
#endregion Packet to Client Mapping
2009-10-06 00:38:14 +00:00
2009-10-06 17:12:59 +00:00
// Stats tracking
2009-10-13 21:50:03 +00:00
Interlocked . Increment ( ref udpClient . PacketsReceived ) ;
2009-10-06 00:38:14 +00:00
2009-10-21 20:47:16 +00:00
int now = Environment . TickCount & Int32 . MaxValue ;
2012-06-12 01:46:14 +00:00
udpClient . TickLastPacketReceived = now ;
2009-10-06 00:38:14 +00:00
2009-10-21 20:47:16 +00:00
#region ACK Receiving
2015-09-22 17:39:59 +00:00
// Handle appended ACKs
if ( packet . Header . AppendedAcks & & packet . Header . AckList ! = null )
2009-09-30 16:00:09 +00:00
{
2015-09-22 17:39:59 +00:00
// m_log.DebugFormat(
// "[LLUDPSERVER]: Handling {0} appended acks from {1} in {2}",
// packet.Header.AckList.Length, client.Name, m_scene.Name);
2012-10-17 22:08:14 +00:00
2015-09-22 17:39:59 +00:00
for ( int i = 0 ; i < packet . Header . AckList . Length ; i + + )
udpClient . NeedAcks . Acknowledge ( packet . Header . AckList [ i ] , now , packet . Header . Resent ) ;
}
2009-10-06 00:38:14 +00:00
2015-09-22 17:39:59 +00:00
// Handle PacketAck packets
if ( packet . Type = = PacketType . PacketAck )
{
PacketAckPacket ackPacket = ( PacketAckPacket ) packet ;
2009-10-06 00:38:14 +00:00
2015-09-22 17:39:59 +00:00
// m_log.DebugFormat(
// "[LLUDPSERVER]: Handling {0} packet acks for {1} in {2}",
// ackPacket.Packets.Length, client.Name, m_scene.Name);
2012-10-17 22:08:14 +00:00
2015-09-22 17:39:59 +00:00
for ( int i = 0 ; i < ackPacket . Packets . Length ; i + + )
udpClient . NeedAcks . Acknowledge ( ackPacket . Packets [ i ] . ID , now , packet . Header . Resent ) ;
2009-10-18 09:00:42 +00:00
2015-09-22 17:39:59 +00:00
// We don't need to do anything else with PacketAck packets
2009-10-18 09:00:42 +00:00
return ;
2009-10-06 00:38:14 +00:00
}
2008-06-04 17:43:07 +00:00
2009-10-06 00:38:14 +00:00
#endregion ACK Receiving
2008-05-02 19:21:33 +00:00
2009-10-06 00:38:14 +00:00
#region ACK Sending
if ( packet . Header . Reliable )
2009-10-18 09:00:42 +00:00
{
2012-10-17 22:08:14 +00:00
// m_log.DebugFormat(
// "[LLUDPSERVER]: Adding ack request for {0} {1} from {2} in {3}",
// packet.Type, packet.Header.Sequence, client.Name, m_scene.Name);
2009-10-13 21:50:03 +00:00
udpClient . PendingAcks . Enqueue ( packet . Header . Sequence ) ;
2009-10-06 00:38:14 +00:00
2009-10-18 09:00:42 +00:00
// This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out,
// add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove
// 2*MTU bytes from the value and send ACKs, and finally add the local value back to
// client.BytesSinceLastACK. Lockless thread safety
int bytesSinceLastACK = Interlocked . Exchange ( ref udpClient . BytesSinceLastACK , 0 ) ;
bytesSinceLastACK + = buffer . DataLength ;
if ( bytesSinceLastACK > LLUDPServer . MTU * 2 )
{
bytesSinceLastACK - = LLUDPServer . MTU * 2 ;
SendAcks ( udpClient ) ;
}
Interlocked . Add ( ref udpClient . BytesSinceLastACK , bytesSinceLastACK ) ;
2009-10-06 00:38:14 +00:00
}
#endregion ACK Sending
#region Incoming Packet Accounting
2013-11-06 01:02:20 +00:00
// We're not going to worry about interlock yet since its not currently critical that this total count
// is 100% correct
if ( packet . Header . Resent )
IncomingPacketsResentCount + + ;
2009-10-06 00:38:14 +00:00
// Check the archive of received reliable packet IDs to see whether we already received this packet
2009-10-13 21:50:03 +00:00
if ( packet . Header . Reliable & & ! udpClient . PacketArchive . TryEnqueue ( packet . Header . Sequence ) )
2009-10-06 00:38:14 +00:00
{
if ( packet . Header . Resent )
2010-09-13 20:28:42 +00:00
m_log . DebugFormat (
2017-01-05 19:07:37 +00:00
"[LLUDPSERVER]: Received a resend of already processed packet #{0}, type {1} from {2}" ,
2010-09-13 20:28:42 +00:00
packet . Header . Sequence , packet . Type , client . Name ) ;
else
m_log . WarnFormat (
"[LLUDPSERVER]: Received a duplicate (not marked as resend) of packet #{0}, type {1} from {2}" ,
packet . Header . Sequence , packet . Type , client . Name ) ;
2009-10-06 00:38:14 +00:00
// Avoid firing a callback twice for the same packet
return ;
2009-09-30 16:00:09 +00:00
}
2008-11-10 09:50:05 +00:00
2009-10-06 00:38:14 +00:00
#endregion Incoming Packet Accounting
2008-11-10 09:50:05 +00:00
2009-11-05 20:01:40 +00:00
#region BinaryStats
LogPacketHeader ( true , udpClient . CircuitCode , 0 , packet . Type , ( ushort ) packet . Length ) ;
#endregion BinaryStats
2009-10-18 09:00:42 +00:00
#region Ping Check Handling
if ( packet . Type = = PacketType . StartPingCheck )
2009-10-06 00:38:14 +00:00
{
2012-10-17 22:08:14 +00:00
// m_log.DebugFormat("[LLUDPSERVER]: Handling ping from {0} in {1}", client.Name, m_scene.Name);
2009-10-18 09:00:42 +00:00
// We don't need to do anything else with ping checks
StartPingCheckPacket startPing = ( StartPingCheckPacket ) packet ;
2009-10-30 07:43:46 +00:00
CompletePing ( udpClient , startPing . PingID . PingID ) ;
2017-01-05 19:07:37 +00:00
2009-11-28 00:17:36 +00:00
if ( ( Environment . TickCount - m_elapsedMSSinceLastStatReport ) > = 3000 )
{
udpClient . SendPacketStats ( ) ;
m_elapsedMSSinceLastStatReport = Environment . TickCount ;
}
2009-10-18 09:00:42 +00:00
return ;
2009-10-06 00:38:14 +00:00
}
2009-10-18 09:00:42 +00:00
else if ( packet . Type = = PacketType . CompletePingCheck )
{
2014-08-14 19:41:36 +00:00
int t = Util . EnvironmentTickCountSubtract ( udpClient . m_lastStartpingTimeMS ) ;
int c = udpClient . m_pingMS ;
2014-08-14 20:04:30 +00:00
c = 800 * c + 200 * t ;
2014-08-14 19:41:36 +00:00
c / = 1000 ;
udpClient . m_pingMS = c ;
2009-10-18 09:00:42 +00:00
return ;
}
#endregion Ping Check Handling
2012-10-17 20:08:15 +00:00
IncomingPacket incomingPacket ;
2019-02-25 21:46:23 +00:00
incomingPacket = new IncomingPacket ( ( LLClientView ) client , packet ) ;
2012-10-17 20:08:15 +00:00
2014-08-12 23:17:03 +00:00
// if (incomingPacket.Packet.Type == PacketType.AgentUpdate ||
// incomingPacket.Packet.Type == PacketType.ChatFromViewer)
2018-01-22 17:09:38 +00:00
// if (incomingPacket.Packet.Type == PacketType.ChatFromViewer)
// packetInbox.PriorityEnqueue(incomingPacket);
// else
// packetInbox.Enqueue(incomingPacket);
packetInbox . Add ( incomingPacket ) ;
2009-10-06 00:38:14 +00:00
}
2008-11-10 09:50:05 +00:00
2009-11-05 20:01:40 +00:00
#region BinaryStats
public class PacketLogger
{
public DateTime StartTime ;
public string Path = null ;
public System . IO . BinaryWriter Log = null ;
}
public static PacketLogger PacketLog ;
protected static bool m_shouldCollectStats = false ;
// Number of seconds to log for
static TimeSpan binStatsMaxFilesize = TimeSpan . FromSeconds ( 300 ) ;
static object binStatsLogLock = new object ( ) ;
static string binStatsDir = "" ;
2013-03-15 20:59:34 +00:00
//for Aggregated In/Out BW logging
static bool m_aggregatedBWStats = false ;
static long m_aggregatedBytesIn = 0 ;
static long m_aggregatedByestOut = 0 ;
static object aggBWStatsLock = new object ( ) ;
public static long AggregatedLLUDPBytesIn
{
get { return m_aggregatedBytesIn ; }
}
public static long AggregatedLLUDPBytesOut
{
get { return m_aggregatedByestOut ; }
}
2009-11-05 20:01:40 +00:00
public static void LogPacketHeader ( bool incoming , uint circuit , byte flags , PacketType packetType , ushort size )
{
2013-03-15 20:59:34 +00:00
if ( m_aggregatedBWStats )
{
lock ( aggBWStatsLock )
{
if ( incoming )
m_aggregatedBytesIn + = size ;
else
m_aggregatedByestOut + = size ;
}
}
2009-11-05 20:01:40 +00:00
if ( ! m_shouldCollectStats ) return ;
// Binary logging format is TTTTTTTTCCCCFPPPSS, T=Time, C=Circuit, F=Flags, P=PacketType, S=size
// Put the incoming bit into the least significant bit of the flags byte
if ( incoming )
flags | = 0x01 ;
else
flags & = 0xFE ;
// Put the flags byte into the most significant bits of the type integer
uint type = ( uint ) packetType ;
type | = ( uint ) flags < < 24 ;
// m_log.Debug("1 LogPacketHeader(): Outside lock");
lock ( binStatsLogLock )
{
DateTime now = DateTime . Now ;
// m_log.Debug("2 LogPacketHeader(): Inside lock. now is " + now.Ticks);
try
{
if ( PacketLog = = null | | ( now > PacketLog . StartTime + binStatsMaxFilesize ) )
{
if ( PacketLog ! = null & & PacketLog . Log ! = null )
{
PacketLog . Log . Close ( ) ;
}
// First log file or time has expired, start writing to a new log file
PacketLog = new PacketLogger ( ) ;
PacketLog . StartTime = now ;
PacketLog . Path = ( binStatsDir . Length > 0 ? binStatsDir + System . IO . Path . DirectorySeparatorChar . ToString ( ) : "" )
+ String . Format ( "packets-{0}.log" , now . ToString ( "yyyyMMddHHmmss" ) ) ;
PacketLog . Log = new BinaryWriter ( File . Open ( PacketLog . Path , FileMode . Append , FileAccess . Write ) ) ;
}
// Serialize the data
byte [ ] output = new byte [ 18 ] ;
Buffer . BlockCopy ( BitConverter . GetBytes ( now . Ticks ) , 0 , output , 0 , 8 ) ;
Buffer . BlockCopy ( BitConverter . GetBytes ( circuit ) , 0 , output , 8 , 4 ) ;
Buffer . BlockCopy ( BitConverter . GetBytes ( type ) , 0 , output , 12 , 4 ) ;
Buffer . BlockCopy ( BitConverter . GetBytes ( size ) , 0 , output , 16 , 2 ) ;
// Write the serialized data to disk
if ( PacketLog ! = null & & PacketLog . Log ! = null )
PacketLog . Log . Write ( output ) ;
}
catch ( Exception ex )
{
m_log . Error ( "Packet statistics gathering failed: " + ex . Message , ex ) ;
if ( PacketLog . Log ! = null )
{
PacketLog . Log . Close ( ) ;
}
PacketLog = null ;
}
}
}
#endregion BinaryStats
2016-06-13 23:21:47 +00:00
protected void HandleUseCircuitCode ( object o )
2009-10-26 20:38:07 +00:00
{
2012-10-16 22:35:05 +00:00
IPEndPoint endPoint = null ;
2012-02-16 01:39:12 +00:00
IClientAPI client = null ;
2009-10-26 20:38:07 +00:00
2012-02-16 01:25:54 +00:00
try
2011-12-08 22:00:59 +00:00
{
2014-08-13 00:42:16 +00:00
// DateTime startTime = DateTime.Now;
2012-02-16 01:25:54 +00:00
object [ ] array = ( object [ ] ) o ;
2012-10-16 22:35:05 +00:00
endPoint = ( IPEndPoint ) array [ 0 ] ;
2012-02-16 01:25:54 +00:00
UseCircuitCodePacket uccp = ( UseCircuitCodePacket ) array [ 1 ] ;
2012-04-26 21:35:25 +00:00
m_log . DebugFormat (
2012-06-15 04:01:36 +00:00
"[LLUDPSERVER]: Handling UseCircuitCode request for circuit {0} to {1} from IP {2}" ,
2014-08-18 21:51:36 +00:00
uccp . CircuitCode . Code , Scene . RegionInfo . RegionName , endPoint ) ;
2017-01-05 19:07:37 +00:00
2012-02-16 01:25:54 +00:00
AuthenticateResponse sessionInfo ;
if ( IsClientAuthorized ( uccp , out sessionInfo ) )
{
2015-09-02 18:54:53 +00:00
AgentCircuitData aCircuit = Scene . AuthenticateHandler . GetAgentCircuitData ( uccp . CircuitCode . Code ) ;
2015-06-12 01:16:09 +00:00
2012-02-16 01:25:54 +00:00
// Begin the process of adding the client to the simulator
client
= AddClient (
uccp . CircuitCode . Code ,
uccp . CircuitCode . ID ,
uccp . CircuitCode . SessionID ,
2012-10-16 22:35:05 +00:00
endPoint ,
2012-02-16 01:25:54 +00:00
sessionInfo ) ;
2014-09-14 01:28:42 +00:00
2015-06-12 01:16:09 +00:00
// This will be true if the client is new, e.g. not
// an existing child agent, and there is no circuit data
if ( client ! = null & & aCircuit = = null )
{
2015-09-02 18:54:53 +00:00
Scene . CloseAgent ( client . AgentId , true ) ;
2015-06-12 01:16:09 +00:00
return ;
}
2014-09-14 01:28:42 +00:00
// Now we know we can handle more data
Thread . Sleep ( 200 ) ;
2014-08-04 20:52:29 +00:00
// Obtain the pending queue and remove it from the cache
2012-07-15 19:45:06 +00:00
Queue < UDPPacketBuffer > queue = null ;
lock ( m_pendingCache )
{
2012-10-16 23:03:02 +00:00
if ( ! m_pendingCache . TryGetValue ( endPoint , out queue ) )
2012-07-15 20:28:47 +00:00
{
m_log . DebugFormat ( "[LLUDPSERVER]: Client created but no pending queue present" ) ;
2012-07-15 19:45:06 +00:00
return ;
2012-07-15 20:28:47 +00:00
}
2012-10-16 23:03:02 +00:00
m_pendingCache . Remove ( endPoint ) ;
2012-07-15 19:45:06 +00:00
}
2019-01-02 16:44:13 +00:00
client . CheckViewerCaps ( ) ;
2012-07-15 20:28:47 +00:00
2019-01-02 16:44:13 +00:00
m_log . DebugFormat ( "[LLUDPSERVER]: Client created, processing pending queue, {0} entries" , queue . Count ) ;
2012-07-15 19:45:06 +00:00
// Reinject queued packets
2014-09-14 01:28:42 +00:00
while ( queue . Count > 0 )
2012-07-15 19:45:06 +00:00
{
2014-09-14 01:28:42 +00:00
UDPPacketBuffer buf = queue . Dequeue ( ) ;
PacketReceived ( buf ) ;
2014-09-13 19:37:20 +00:00
}
2014-08-04 20:52:29 +00:00
2014-09-14 01:28:42 +00:00
queue = null ;
2012-02-16 01:25:54 +00:00
// Send ack straight away to let the viewer know that the connection is active.
// The client will be null if it already exists (e.g. if on a region crossing the client sends a use
// circuit code to the existing child agent. This is not particularly obvious.
2012-10-16 22:35:05 +00:00
SendAckImmediate ( endPoint , uccp . Header . Sequence ) ;
2014-08-04 20:52:29 +00:00
2013-05-14 15:47:18 +00:00
// We only want to send initial data to new clients, not ones which are being converted from child to root.
if ( client ! = null )
2013-05-14 16:06:58 +00:00
{
bool tp = ( aCircuit . teleportFlags > 0 ) ;
Moved SendInitialDataToMe to earlier in CompleteMovement. Moved TriggerOnMakeRootAgent to the end of CompleteMovement.
Justin, if you read this, there's a long story here. Some time ago you placed SendInitialDataToMe at the very beginning of client creation (in LLUDPServer). That is problematic, as we discovered relatively recently: on TPs, as soon as the client starts getting data from child agents, it starts requesting resources back *from the simulator where its root agent is*. We found this to be the problem behind meshes missing on HG TPs (because the viewer was requesting the meshes of the receiving sim from the departing grid). But this affects much more than meshes and HG TPs. It may also explain cloud avatars after a local TP: baked textures are only stored in the simulator, so if a child agent receives a UUID of a baked texture in the destination sim and requests that texture from the departing sim where the root agent is, it will fail to get that texture.
Bottom line: we need to delay sending the new simulator data to the viewer until we are absolutely sure that the viewer knows that its main agent is in a new sim. Hence, moving it to CompleteMovement.
Now I am trying to tune the initial rez delay that we all experience in the CC. I think that when I fixed the issue described above, I may have moved SendInitialDataToMe to much later than it should be, so now I'm moving to earlier in CompleteMovement.
2013-07-13 16:46:58 +00:00
// Let's delay this for TP agents, otherwise the viewer doesn't know where to get resources from
2015-09-02 18:54:53 +00:00
if ( ! tp )
client . SceneAgent . SendInitialDataToMe ( ) ;
2013-05-14 16:06:58 +00:00
}
2012-02-16 01:25:54 +00:00
}
else
{
// Don't create clients for unauthorized requesters.
m_log . WarnFormat (
2012-06-15 04:01:36 +00:00
"[LLUDPSERVER]: Ignoring connection request for {0} to {1} with unknown circuit code {2} from IP {3}" ,
2015-09-02 18:54:53 +00:00
2014-08-18 21:51:36 +00:00
uccp . CircuitCode . ID , Scene . RegionInfo . RegionName , uccp . CircuitCode . Code , endPoint ) ;
2014-09-13 19:37:20 +00:00
2012-07-15 19:45:06 +00:00
lock ( m_pendingCache )
2012-10-16 23:03:02 +00:00
m_pendingCache . Remove ( endPoint ) ;
2017-01-05 19:07:37 +00:00
}
2015-09-02 18:54:53 +00:00
2012-02-16 01:25:54 +00:00
// m_log.DebugFormat(
2017-01-05 19:07:37 +00:00
// "[LLUDPSERVER]: Handling UseCircuitCode request from {0} took {1}ms",
2012-02-16 01:25:54 +00:00
// buffer.RemoteEndPoint, (DateTime.Now - startTime).Milliseconds);
2011-12-08 22:00:59 +00:00
}
2012-02-16 01:25:54 +00:00
catch ( Exception e )
2011-12-08 22:00:59 +00:00
{
2012-02-16 01:25:54 +00:00
m_log . ErrorFormat (
"[LLUDPSERVER]: UseCircuitCode handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}" ,
2012-10-16 22:35:05 +00:00
endPoint ! = null ? endPoint . ToString ( ) : "n/a" ,
2012-02-16 01:25:54 +00:00
client ! = null ? client . Name : "unknown" ,
2012-02-16 01:39:12 +00:00
client ! = null ? client . AgentId . ToString ( ) : "unknown" ,
2012-02-16 01:25:54 +00:00
e . Message ,
e . StackTrace ) ;
2011-12-08 22:00:59 +00:00
}
2009-10-26 20:38:07 +00:00
}
2014-08-13 00:42:16 +00:00
/ *
2016-06-13 23:21:47 +00:00
protected void HandleCompleteMovementIntoRegion ( object o )
New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 18:54:35 +00:00
{
IPEndPoint endPoint = null ;
IClientAPI client = null ;
try
{
object [ ] array = ( object [ ] ) o ;
endPoint = ( IPEndPoint ) array [ 0 ] ;
CompleteAgentMovementPacket packet = ( CompleteAgentMovementPacket ) array [ 1 ] ;
2013-09-25 17:45:56 +00:00
m_log . DebugFormat (
2014-08-18 21:51:36 +00:00
"[LLUDPSERVER]: Handling CompleteAgentMovement request from {0} in {1}" , endPoint , Scene . Name ) ;
2013-09-25 17:45:56 +00:00
New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 18:54:35 +00:00
// Determine which agent this packet came from
2013-09-18 20:41:51 +00:00
// We need to wait here because in when using the OpenSimulator V2 teleport protocol to travel to a destination
2017-01-05 19:07:37 +00:00
// simulator with no existing child presence, the viewer (at least LL 3.3.4) will send UseCircuitCode
2013-09-18 20:41:51 +00:00
// and then CompleteAgentMovement immediately without waiting for an ack. As we are now handling these
2017-01-05 19:07:37 +00:00
// packets asynchronously, we need to account for this thread proceeding more quickly than the
2013-09-18 20:41:51 +00:00
// UseCircuitCode thread.
2013-09-18 21:09:46 +00:00
int count = 40 ;
2013-09-18 20:41:51 +00:00
while ( count - - > 0 )
New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 18:54:35 +00:00
{
2014-08-18 21:51:36 +00:00
if ( Scene . TryGetClient ( endPoint , out client ) )
2013-07-23 03:20:48 +00:00
{
2013-09-25 17:45:56 +00:00
if ( ! client . IsActive )
2013-07-23 03:20:48 +00:00
{
2013-09-18 20:41:51 +00:00
// This check exists to catch a condition where the client has been closed by another thread
// but has not yet been removed from the client manager (and possibly a new connection has
// not yet been established).
m_log . DebugFormat (
2013-09-25 17:45:56 +00:00
"[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client is not active yet. Waiting." ,
2014-08-18 21:51:36 +00:00
endPoint , client . Name , Scene . Name ) ;
2013-09-25 17:45:56 +00:00
}
else if ( client . SceneAgent = = null )
{
// This check exists to catch a condition where the new client has been added to the client
2013-09-27 21:27:39 +00:00
// manager but the SceneAgent has not yet been set in Scene.AddNewAgent(). If we are too
2013-09-25 17:45:56 +00:00
// eager, then the new ScenePresence may not have registered a listener for this messsage
// before we try to process it.
2017-01-05 19:07:37 +00:00
// XXX: A better long term fix may be to add the SceneAgent before the client is added to
2013-09-25 17:45:56 +00:00
// the client manager
m_log . DebugFormat (
"[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client SceneAgent not set yet. Waiting." ,
2014-08-18 21:51:36 +00:00
endPoint , client . Name , Scene . Name ) ;
2013-07-23 03:20:48 +00:00
}
2013-09-25 17:45:56 +00:00
else
{
break ;
}
2013-07-23 03:20:48 +00:00
}
else
{
2013-09-18 20:41:51 +00:00
m_log . DebugFormat (
2017-01-05 19:07:37 +00:00
"[LLUDPSERVER]: Received a CompleteAgentMovement from {0} in {1} but no client exists yet. Waiting." ,
2014-08-18 21:51:36 +00:00
endPoint , Scene . Name ) ;
2013-07-23 03:20:48 +00:00
}
2013-09-25 17:29:14 +00:00
Thread . Sleep ( 200 ) ;
New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 18:54:35 +00:00
}
if ( client = = null )
2013-09-18 20:41:51 +00:00
{
m_log . DebugFormat (
2013-09-25 17:45:56 +00:00
"[LLUDPSERVER]: No client found for CompleteAgentMovement from {0} in {1} after wait. Dropping." ,
2014-08-18 21:51:36 +00:00
endPoint , Scene . Name ) ;
2013-09-18 20:41:51 +00:00
New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 18:54:35 +00:00
return ;
2013-09-18 20:41:51 +00:00
}
2013-09-25 17:45:56 +00:00
else if ( ! client . IsActive | | client . SceneAgent = = null )
2013-09-18 20:41:51 +00:00
{
// This check exists to catch a condition where the client has been closed by another thread
// but has not yet been removed from the client manager.
// The packet could be simply ignored but it is useful to know if this condition occurred for other debugging
// purposes.
m_log . DebugFormat (
2013-09-25 17:45:56 +00:00
"[LLUDPSERVER]: Received a CompleteAgentMovement from {0} for {1} in {2} but client is not active after wait. Dropping." ,
2014-08-18 21:51:36 +00:00
endPoint , client . Name , Scene . Name ) ;
2013-09-18 20:41:51 +00:00
return ;
}
New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 18:54:35 +00:00
IncomingPacket incomingPacket1 ;
// Inbox insertion
if ( UsePools )
{
incomingPacket1 = m_incomingPacketPool . GetObject ( ) ;
incomingPacket1 . Client = ( LLClientView ) client ;
incomingPacket1 . Packet = packet ;
}
else
{
incomingPacket1 = new IncomingPacket ( ( LLClientView ) client , packet ) ;
}
packetInbox . Enqueue ( incomingPacket1 ) ;
}
catch ( Exception e )
{
m_log . ErrorFormat (
2013-09-25 17:45:56 +00:00
"[LLUDPSERVER]: CompleteAgentMovement handling from endpoint {0}, client {1} {2} failed. Exception {3}{4}" ,
New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 18:54:35 +00:00
endPoint ! = null ? endPoint . ToString ( ) : "n/a" ,
client ! = null ? client . Name : "unknown" ,
client ! = null ? client . AgentId . ToString ( ) : "unknown" ,
e . Message ,
e . StackTrace ) ;
}
}
2014-08-13 00:42:16 +00:00
* /
New Teleport protocol (V2), still compatible with V1 and older. (version of the destination is being checked)
In this new protocol, and as committed before, the viewer is not sent EnableSimulator/EstablishChildCommunication for the destination. Instead, it is sent TeleportFinish directly. TeleportFinish, in turn, makes the viewer send a UserCircuitCode packet followed by CompleteMovementIntoRegion packet. These 2 packets tend to occur one after the other almost immediately to the point that when CMIR arrives the client is not even connected yet and that packet is ignored (there might have been some race conditions here before); then the viewer sends CMIR again within 5-8 secs. But the delay between them may be higher in busier regions, which may lead to race conditions.
This commit improves the process so there are are no race conditions at the destination. CompleteMovement (triggered by the viewer) waits until Update has been sent from the origin. Update, in turn, waits until there is a *root* scene presence -- so making sure CompleteMovement has run MakeRoot. In other words, there are two threadlets at the destination, one from the viewer and one from the origin region, waiting for each other to do the right thing. That makes it safe to close the agent at the origin upon return of the Update call without having to wait for callback, because we are absolutely sure that the viewer knows it is in th new region.
Note also that in the V1 protocol, the destination was getting UseCircuitCode from the viewer twice -- once on EstablishAgentCommunication and then again on TeleportFinish. The second UCC was being ignored, but it shows how we were not following the expected steps...
2013-07-22 18:54:35 +00:00
2011-12-08 21:45:02 +00:00
/// <summary>
/// Send an ack immediately to the given endpoint.
/// </summary>
/// <remarks>
/// FIXME: Might be possible to use SendPacketData() like everything else, but this will require refactoring so
/// that we can obtain the UDPClient easily at this point.
/// </remarks>
/// <param name="remoteEndpoint"></param>
/// <param name="sequenceNumber"></param>
2016-06-13 23:21:47 +00:00
protected void SendAckImmediate ( IPEndPoint remoteEndpoint , uint sequenceNumber )
2009-10-21 20:47:16 +00:00
{
PacketAckPacket ack = new PacketAckPacket ( ) ;
ack . Header . Reliable = false ;
ack . Packets = new PacketAckPacket . PacketsBlock [ 1 ] ;
ack . Packets [ 0 ] = new PacketAckPacket . PacketsBlock ( ) ;
ack . Packets [ 0 ] . ID = sequenceNumber ;
2011-12-08 21:45:02 +00:00
SendAckImmediate ( remoteEndpoint , ack ) ;
}
public virtual void SendAckImmediate ( IPEndPoint remoteEndpoint , PacketAckPacket ack )
{
2009-10-21 20:47:16 +00:00
byte [ ] packetData = ack . ToBytes ( ) ;
int length = packetData . Length ;
2019-02-25 21:46:23 +00:00
UDPPacketBuffer buffer = GetNewUDPBuffer ( remoteEndpoint ) ;
2009-10-21 20:47:16 +00:00
buffer . DataLength = length ;
Buffer . BlockCopy ( packetData , 0 , buffer . Data , 0 , length ) ;
2017-06-13 11:17:39 +00:00
// AsyncBeginSend(buffer);
SyncSend ( buffer ) ;
2019-02-25 21:46:23 +00:00
FreeUDPBuffer ( buffer ) ;
2009-10-21 20:47:16 +00:00
}
2016-06-13 23:21:47 +00:00
protected bool IsClientAuthorized ( UseCircuitCodePacket useCircuitCode , out AuthenticateResponse sessionInfo )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
UUID agentID = useCircuitCode . CircuitCode . ID ;
UUID sessionID = useCircuitCode . CircuitCode . SessionID ;
uint circuitCode = useCircuitCode . CircuitCode . Code ;
2008-05-02 19:21:33 +00:00
2009-10-06 00:38:14 +00:00
sessionInfo = m_circuitManager . AuthenticateSession ( sessionID , agentID , circuitCode ) ;
return sessionInfo . Authorised ;
}
2008-06-03 13:49:58 +00:00
2011-12-08 18:34:23 +00:00
/// <summary>
/// Add a client.
/// </summary>
/// <param name="circuitCode"></param>
/// <param name="agentID"></param>
/// <param name="sessionID"></param>
/// <param name="remoteEndPoint"></param>
/// <param name="sessionInfo"></param>
/// <returns>The client if it was added. Null if the client already existed.</returns>
protected virtual IClientAPI AddClient (
uint circuitCode , UUID agentID , UUID sessionID , IPEndPoint remoteEndPoint , AuthenticateResponse sessionInfo )
2008-05-02 19:21:33 +00:00
{
2011-12-08 18:34:23 +00:00
IClientAPI client = null ;
2012-10-09 23:26:43 +00:00
// We currently synchronize this code across the whole scene to avoid issues such as
// http://opensimulator.org/mantis/view.php?id=5365 However, once locking per agent circuit can be done
// consistently, this lock could probably be removed.
lock ( this )
2009-10-13 21:50:03 +00:00
{
2019-01-01 12:06:29 +00:00
if ( Scene . TryGetClient ( agentID , out client ) )
2011-02-09 01:53:01 +00:00
{
2019-01-01 12:06:29 +00:00
if ( client . SceneAgent ! = null )
return client ;
Scene . CloseAgent ( agentID , true ) ;
2014-09-12 22:49:32 +00:00
}
2019-01-01 12:06:29 +00:00
LLUDPClient udpClient = new LLUDPClient ( this , ThrottleRates , Throttle , circuitCode , agentID , remoteEndPoint , m_defaultRTO , m_maxRTO ) ;
2015-09-02 18:54:53 +00:00
2019-01-01 12:06:29 +00:00
client = new LLClientView ( Scene , this , udpClient , sessionInfo , agentID , sessionID , circuitCode ) ;
client . OnLogout + = LogoutHandler ;
client . DebugPacketLevel = DefaultClientPacketDebugLevel ;
2017-01-05 19:07:37 +00:00
2019-01-01 12:06:29 +00:00
( ( LLClientView ) client ) . DisableFacelights = m_disableFacelights ;
2017-01-05 19:07:37 +00:00
2019-01-01 12:06:29 +00:00
client . Start ( ) ;
2009-10-13 21:50:03 +00:00
}
2011-12-08 18:34:23 +00:00
return client ;
2009-10-13 21:50:03 +00:00
}
2012-06-12 01:16:36 +00:00
/// <summary>
/// Deactivates the client if we don't receive any packets within a certain amount of time (default 60 seconds).
/// </summary>
/// <remarks>
/// If a connection is active then we will always receive packets even if nothing else is happening, due to
/// regular client pings.
/// </remarks>
/// <param name='client'></param>
2013-07-29 22:53:59 +00:00
/// <param name='timeoutTicks'></param>
2016-06-13 23:21:47 +00:00
protected void DeactivateClientDueToTimeout ( LLClientView client , int timeoutTicks )
2009-10-13 21:50:03 +00:00
{
2012-07-19 21:32:27 +00:00
lock ( client . CloseSyncLock )
2017-01-05 19:07:37 +00:00
{
2013-07-29 22:18:29 +00:00
ClientLogoutsDueToNoReceives + + ;
2013-07-29 22:53:59 +00:00
2014-09-15 21:15:27 +00:00
if ( client . SceneAgent ! = null )
{
m_log . WarnFormat (
"[LLUDPSERVER]: No packets received from {0} agent of {1} for {2}ms in {3}. Disconnecting." ,
2015-09-02 18:54:53 +00:00
client . SceneAgent . IsChildAgent ? "child" : "root" , client . Name , timeoutTicks , Scene . Name ) ;
2017-01-05 19:07:37 +00:00
2014-09-15 21:15:27 +00:00
if ( ! client . SceneAgent . IsChildAgent )
client . Kick ( "Simulator logged you out due to connection timeout." ) ;
}
2012-07-19 21:32:27 +00:00
}
2013-08-08 22:29:30 +00:00
2015-09-02 18:54:53 +00:00
if ( ! Scene . CloseAgent ( client . AgentId , true ) )
2014-09-14 18:46:22 +00:00
client . Close ( true , true ) ;
2012-06-12 01:03:31 +00:00
}
2016-06-13 23:21:47 +00:00
protected void IncomingPacketHandler ( )
2009-10-06 00:38:14 +00:00
{
2015-12-09 16:26:03 +00:00
IncomingPacket incomingPacket ;
2009-10-06 09:38:00 +00:00
// Set this culture for the thread that incoming packets are received
// on to en-US to avoid number parsing issues
Culture . SetCurrentCulture ( ) ;
2012-10-17 22:08:14 +00:00
while ( IsRunningInbound )
2008-05-02 19:21:33 +00:00
{
2015-09-02 18:54:53 +00:00
Scene . ThreadAlive ( 1 ) ;
2009-10-18 00:19:18 +00:00
try
{
2019-02-25 21:46:23 +00:00
packetInbox . TryTake ( out incomingPacket , 4500 ) ;
2017-01-05 19:07:37 +00:00
2015-12-09 16:42:24 +00:00
if ( incomingPacket ! = null & & IsRunningInbound )
2012-10-17 20:08:15 +00:00
{
2015-12-09 16:42:24 +00:00
ProcessInPacket ( incomingPacket ) ;
incomingPacket = null ;
}
2009-10-18 00:19:18 +00:00
}
2019-02-27 15:49:52 +00:00
catch ( ThreadAbortException )
{
Thread . ResetAbort ( ) ;
}
catch ( Exception ex )
2009-10-18 00:19:18 +00:00
{
m_log . Error ( "[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex . Message , ex ) ;
}
2009-10-22 19:33:23 +00:00
Watchdog . UpdateThread ( ) ;
2008-05-02 19:21:33 +00:00
}
2009-10-06 00:38:14 +00:00
2018-01-22 17:09:38 +00:00
if ( packetInbox . Count > 0 )
m_log . Warn ( "[LLUDPSERVER]: IncomingPacketHandler is shutting down, dropping " + packetInbox . Count + " packets" ) ;
packetInbox . Dispose ( ) ;
2009-10-22 19:33:23 +00:00
Watchdog . RemoveThread ( ) ;
2008-05-02 19:21:33 +00:00
}
2016-06-13 23:21:47 +00:00
protected void OutgoingPacketHandler ( )
2008-05-02 19:21:33 +00:00
{
2009-10-06 09:38:00 +00:00
// Set this culture for the thread that outgoing packets are sent
// on to en-US to avoid number parsing issues
Culture . SetCurrentCulture ( ) ;
2009-10-23 20:14:29 +00:00
// Typecast the function to an Action<IClientAPI> once here to avoid allocating a new
// Action generic every round
Action < IClientAPI > clientPacketHandler = ClientOutgoingPacketHandler ;
2013-07-18 21:42:25 +00:00
while ( base . IsRunningOutbound )
2008-07-11 17:58:26 +00:00
{
2015-09-02 18:54:53 +00:00
Scene . ThreadAlive ( 2 ) ;
2016-07-12 22:23:47 +00:00
2009-10-20 19:43:09 +00:00
try
2009-10-06 09:38:00 +00:00
{
2009-10-21 18:59:48 +00:00
m_packetSent = false ;
2009-10-06 00:38:14 +00:00
2009-10-21 22:22:23 +00:00
#region Update Timers
m_resendUnacked = false ;
m_sendAcks = false ;
m_sendPing = false ;
// Update elapsed time
2016-11-18 03:25:29 +00:00
double thisTick = Util . GetTimeStampMS ( ) ;
2009-10-21 22:22:23 +00:00
2016-11-09 10:21:02 +00:00
// update some 1ms resolution chained timers
2016-11-18 03:25:29 +00:00
m_elapsedMSOutgoingPacketHandler + = thisTick - m_tickLastOutgoingPacketHandler ;
m_tickLastOutgoingPacketHandler = thisTick ;
2016-11-09 10:21:02 +00:00
2009-10-21 22:22:23 +00:00
// Check for pending outgoing resends every 100ms
2016-11-18 03:25:29 +00:00
if ( m_elapsedMSOutgoingPacketHandler > = 100.0 )
2009-10-21 22:22:23 +00:00
{
m_resendUnacked = true ;
2016-11-18 03:25:29 +00:00
m_elapsedMSOutgoingPacketHandler = 0.0 ;
2009-10-21 22:22:23 +00:00
m_elapsed100MSOutgoingPacketHandler + = 1 ;
}
// Check for pending outgoing ACKs every 500ms
if ( m_elapsed100MSOutgoingPacketHandler > = 5 )
{
m_sendAcks = true ;
m_elapsed100MSOutgoingPacketHandler = 0 ;
m_elapsed500MSOutgoingPacketHandler + = 1 ;
}
// Send pings to clients every 5000ms
if ( m_elapsed500MSOutgoingPacketHandler > = 10 )
{
m_sendPing = true ;
m_elapsed500MSOutgoingPacketHandler = 0 ;
}
#endregion Update Timers
2009-10-21 07:18:35 +00:00
// Handle outgoing packets, resends, acknowledgements, and pings for each
2009-10-21 18:59:48 +00:00
// client. m_packetSent will be set to true if a packet is sent
2014-08-18 21:51:36 +00:00
Scene . ForEachClient ( clientPacketHandler ) ;
2009-10-20 19:43:09 +00:00
2009-10-21 21:03:49 +00:00
// If nothing was sent, sleep for the minimum amount of time before a
// token bucket could get more tokens
2014-08-10 13:49:58 +00:00
2016-07-12 22:23:47 +00:00
if ( Scene . GetNumberOfClients ( ) = = 0 )
{
2016-11-09 10:21:02 +00:00
Thread . Sleep ( 100 ) ;
2016-07-12 22:23:47 +00:00
}
else if ( ! m_packetSent )
// Thread.Sleep((int)TickCountResolution); outch this is bad on linux
2019-02-26 15:02:57 +00:00
Thread . Sleep ( 15 ) ; // match the 16ms of windows, dont ask 16 or win may decide to do 32ms.
2009-10-22 19:33:23 +00:00
Watchdog . UpdateThread ( ) ;
2009-10-20 21:41:20 +00:00
}
catch ( Exception ex )
{
m_log . Error ( "[LLUDPSERVER]: OutgoingPacketHandler loop threw an exception: " + ex . Message , ex ) ;
}
}
2009-10-22 19:33:23 +00:00
Watchdog . RemoveThread ( ) ;
2009-10-20 21:41:20 +00:00
}
2009-10-13 21:50:03 +00:00
2012-06-08 02:12:23 +00:00
protected void ClientOutgoingPacketHandler ( IClientAPI client )
2009-10-20 21:41:20 +00:00
{
try
{
if ( client is LLClientView )
{
2012-06-08 02:53:03 +00:00
LLClientView llClient = ( LLClientView ) client ;
LLUDPClient udpClient = llClient . UDPClient ;
2009-10-20 21:41:20 +00:00
if ( udpClient . IsConnected )
{
2019-02-26 15:02:57 +00:00
if ( client . IsActive & & m_resendUnacked )
2012-06-08 02:53:03 +00:00
HandleUnacked ( llClient ) ;
2009-10-20 21:41:20 +00:00
2019-02-26 15:02:57 +00:00
if ( client . IsActive )
{
if ( m_sendAcks )
SendAcks ( udpClient ) ;
2009-10-21 07:18:35 +00:00
2019-02-26 15:02:57 +00:00
if ( m_sendPing )
SendPing ( udpClient ) ;
}
2009-10-06 00:38:14 +00:00
2009-10-20 21:41:20 +00:00
// Dequeue any outgoing packets that are within the throttle limits
2009-10-21 18:59:48 +00:00
if ( udpClient . DequeueOutgoing ( ) )
m_packetSent = true ;
2009-10-20 21:41:20 +00:00
}
2009-10-20 19:43:09 +00:00
}
2008-05-02 19:21:33 +00:00
}
2009-10-20 21:41:20 +00:00
catch ( Exception ex )
{
2012-06-07 01:44:13 +00:00
m_log . Error (
string . Format ( "[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw " , client . Name ) , ex ) ;
2009-10-20 21:41:20 +00:00
}
2008-05-02 19:21:33 +00:00
}
2011-02-08 20:06:14 +00:00
#region Emergency Monitoring
2011-02-09 17:50:26 +00:00
// Alternative packet handler fuull of instrumentation
// Handy for hunting bugs
2016-06-13 23:21:47 +00:00
protected Stopwatch watch1 = new Stopwatch ( ) ;
protected Stopwatch watch2 = new Stopwatch ( ) ;
protected float avgProcessingTicks = 0 ;
protected float avgResendUnackedTicks = 0 ;
protected float avgSendAcksTicks = 0 ;
protected float avgSendPingTicks = 0 ;
protected float avgDequeueTicks = 0 ;
protected long nticks = 0 ;
protected long nticksUnack = 0 ;
protected long nticksAck = 0 ;
protected long nticksPing = 0 ;
protected int npacksSent = 0 ;
protected int npackNotSent = 0 ;
2011-02-08 20:06:14 +00:00
2012-11-15 01:14:18 +00:00
/// <summary>
/// Number of inbound packets processed since startup.
/// </summary>
2016-06-13 23:21:47 +00:00
public long IncomingPacketsProcessed { get ; protected set ; }
2012-11-15 01:14:18 +00:00
2017-01-05 19:07:37 +00:00
# endregion
2011-02-08 20:06:14 +00:00
2016-06-13 23:21:47 +00:00
protected void ProcessInPacket ( IncomingPacket incomingPacket )
2009-10-06 19:13:16 +00:00
{
Packet packet = incomingPacket . Packet ;
2012-06-08 00:43:58 +00:00
LLClientView client = incomingPacket . Client ;
2009-10-06 19:13:16 +00:00
2015-12-09 22:57:13 +00:00
if ( ! client . IsActive )
return ;
2012-06-07 01:44:13 +00:00
2015-12-09 22:57:13 +00:00
try
{
// Process this packet
client . ProcessInPacket ( packet ) ;
}
catch ( ThreadAbortException )
{
// If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down
m_log . Info ( "[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server" ) ;
Stop ( ) ;
}
catch ( Exception e )
{
// Don't let a failure in an individual client thread crash the whole sim.
m_log . Error (
string . Format (
"[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw " ,
client . Name , packet . Type ) ,
e ) ;
}
2012-11-15 01:14:18 +00:00
IncomingPacketsProcessed + + ;
2009-10-06 19:13:16 +00:00
}
2009-10-27 10:51:47 +00:00
protected void LogoutHandler ( IClientAPI client )
2008-05-02 19:21:33 +00:00
{
2009-10-06 00:38:14 +00:00
client . SendLogoutPacket ( ) ;
2012-06-12 01:16:36 +00:00
2012-06-08 03:12:22 +00:00
if ( ! client . IsLoggingOut )
2012-06-12 01:16:36 +00:00
{
client . IsLoggingOut = true ;
2014-08-18 21:51:36 +00:00
Scene . CloseAgent ( client . AgentId , false ) ;
2012-06-12 01:16:36 +00:00
}
2009-10-13 19:50:59 +00:00
}
2008-05-02 19:21:33 +00:00
}
2014-06-26 13:16:51 +00:00
}