aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack/LindenUDP
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ClientStack/LindenUDP')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs39
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs668
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs76
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs242
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs282
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs420
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs520
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs97
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs9
9 files changed, 944 insertions, 1409 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
index 5877779..b53a2fb 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/J2KImage.cs
@@ -31,6 +31,7 @@ using OpenMetaverse;
31using OpenMetaverse.Imaging; 31using OpenMetaverse.Imaging;
32using OpenSim.Framework; 32using OpenSim.Framework;
33using OpenSim.Region.Framework.Interfaces; 33using OpenSim.Region.Framework.Interfaces;
34using OpenSim.Region.Framework.Scenes.Hypergrid;
34using OpenSim.Services.Interfaces; 35using OpenSim.Services.Interfaces;
35using log4net; 36using log4net;
36using System.Reflection; 37using System.Reflection;
@@ -54,6 +55,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
54 public UUID TextureID; 55 public UUID TextureID;
55 public IJ2KDecoder J2KDecoder; 56 public IJ2KDecoder J2KDecoder;
56 public IAssetService AssetService; 57 public IAssetService AssetService;
58 public UUID AgentID;
59 public IHyperAssetService HyperAssets;
57 public OpenJPEG.J2KLayerInfo[] Layers; 60 public OpenJPEG.J2KLayerInfo[] Layers;
58 public bool IsDecoded; 61 public bool IsDecoded;
59 public bool HasAsset; 62 public bool HasAsset;
@@ -72,14 +75,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
72 m_imageManager = imageManager; 75 m_imageManager = imageManager;
73 } 76 }
74 77
75 public bool SendPackets(LLClientView client, int maxpack) 78 /// <summary>
79 /// Sends packets for this texture to a client until packetsToSend is
80 /// hit or the transfer completes
81 /// </summary>
82 /// <param name="client">Reference to the client that the packets are destined for</param>
83 /// <param name="packetsToSend">Maximum number of packets to send during this call</param>
84 /// <param name="packetsSent">Number of packets sent during this call</param>
85 /// <returns>True if the transfer completes at the current discard level, otherwise false</returns>
86 public bool SendPackets(LLClientView client, int packetsToSend, out int packetsSent)
76 { 87 {
77 if (client == null) 88 packetsSent = 0;
78 return false;
79 89
80 if (m_currentPacket <= m_stopPacket) 90 if (m_currentPacket <= m_stopPacket)
81 { 91 {
82 int count = 0;
83 bool sendMore = true; 92 bool sendMore = true;
84 93
85 if (!m_sentInfo || (m_currentPacket == 0)) 94 if (!m_sentInfo || (m_currentPacket == 0))
@@ -88,25 +97,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP
88 97
89 m_sentInfo = true; 98 m_sentInfo = true;
90 ++m_currentPacket; 99 ++m_currentPacket;
91 ++count; 100 ++packetsSent;
92 } 101 }
93 if (m_currentPacket < 2) 102 if (m_currentPacket < 2)
94 { 103 {
95 m_currentPacket = 2; 104 m_currentPacket = 2;
96 } 105 }
97 106
98 while (sendMore && count < maxpack && m_currentPacket <= m_stopPacket) 107 while (sendMore && packetsSent < packetsToSend && m_currentPacket <= m_stopPacket)
99 { 108 {
100 sendMore = SendPacket(client); 109 sendMore = SendPacket(client);
101 ++m_currentPacket; 110 ++m_currentPacket;
102 ++count; 111 ++packetsSent;
103 } 112 }
104
105 if (m_currentPacket > m_stopPacket)
106 return true;
107 } 113 }
108 114
109 return false; 115 return (m_currentPacket > m_stopPacket);
110 } 116 }
111 117
112 public void RunUpdate() 118 public void RunUpdate()
@@ -370,6 +376,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
370 UUID assetID = UUID.Zero; 376 UUID assetID = UUID.Zero;
371 if (asset != null) 377 if (asset != null)
372 assetID = asset.FullID; 378 assetID = asset.FullID;
379 else if ((HyperAssets != null) && (sender != HyperAssets))
380 {
381 // Try the user's inventory, but only if it's different from the regions'
382 string userAssets = HyperAssets.GetUserAssetServer(AgentID);
383 if ((userAssets != string.Empty) && (userAssets != HyperAssets.GetSimAssetServer()))
384 {
385 m_log.DebugFormat("[J2KIMAGE]: texture {0} not found in local asset storage. Trying user's storage.", id);
386 AssetService.Get(userAssets + "/" + id, HyperAssets, AssetReceived);
387 return;
388 }
389 }
373 390
374 AssetDataCallback(assetID, asset); 391 AssetDataCallback(assetID, asset);
375 392
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 84e705a..8487adc 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -43,6 +43,7 @@ using OpenSim.Framework.Communications.Cache;
43using OpenSim.Framework.Statistics; 43using OpenSim.Framework.Statistics;
44using OpenSim.Region.Framework.Interfaces; 44using OpenSim.Region.Framework.Interfaces;
45using OpenSim.Region.Framework.Scenes; 45using OpenSim.Region.Framework.Scenes;
46using OpenSim.Region.Framework.Scenes.Hypergrid;
46using OpenSim.Services.Interfaces; 47using OpenSim.Services.Interfaces;
47using Timer=System.Timers.Timer; 48using Timer=System.Timers.Timer;
48using AssetLandmark = OpenSim.Framework.AssetLandmark; 49using AssetLandmark = OpenSim.Framework.AssetLandmark;
@@ -58,6 +59,209 @@ namespace OpenSim.Region.ClientStack.LindenUDP
58 /// </summary> 59 /// </summary>
59 public class LLClientView : IClientAPI, IClientCore, IClientIM, IClientChat, IClientIPEndpoint, IStatsCollector 60 public class LLClientView : IClientAPI, IClientCore, IClientIM, IClientChat, IClientIPEndpoint, IStatsCollector
60 { 61 {
62 #region Events
63
64 public event GenericMessage OnGenericMessage;
65 public event BinaryGenericMessage OnBinaryGenericMessage;
66 public event Action<IClientAPI> OnLogout;
67 public event ObjectPermissions OnObjectPermissions;
68 public event Action<IClientAPI> OnConnectionClosed;
69 public event ViewerEffectEventHandler OnViewerEffect;
70 public event ImprovedInstantMessage OnInstantMessage;
71 public event ChatMessage OnChatFromClient;
72 public event TextureRequest OnRequestTexture;
73 public event RezObject OnRezObject;
74 public event DeRezObject OnDeRezObject;
75 public event ModifyTerrain OnModifyTerrain;
76 public event Action<IClientAPI> OnRegionHandShakeReply;
77 public event GenericCall2 OnRequestWearables;
78 public event SetAppearance OnSetAppearance;
79 public event AvatarNowWearing OnAvatarNowWearing;
80 public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv;
81 public event RezMultipleAttachmentsFromInv OnRezMultipleAttachmentsFromInv;
82 public event UUIDNameRequest OnDetachAttachmentIntoInv;
83 public event ObjectAttach OnObjectAttach;
84 public event ObjectDeselect OnObjectDetach;
85 public event ObjectDrop OnObjectDrop;
86 public event GenericCall2 OnCompleteMovementToRegion;
87 public event UpdateAgent OnAgentUpdate;
88 public event AgentRequestSit OnAgentRequestSit;
89 public event AgentSit OnAgentSit;
90 public event AvatarPickerRequest OnAvatarPickerRequest;
91 public event StartAnim OnStartAnim;
92 public event StopAnim OnStopAnim;
93 public event Action<IClientAPI> OnRequestAvatarsData;
94 public event LinkObjects OnLinkObjects;
95 public event DelinkObjects OnDelinkObjects;
96 public event GrabObject OnGrabObject;
97 public event DeGrabObject OnDeGrabObject;
98 public event SpinStart OnSpinStart;
99 public event SpinStop OnSpinStop;
100 public event ObjectDuplicate OnObjectDuplicate;
101 public event ObjectDuplicateOnRay OnObjectDuplicateOnRay;
102 public event MoveObject OnGrabUpdate;
103 public event SpinObject OnSpinUpdate;
104 public event AddNewPrim OnAddPrim;
105 public event RequestGodlikePowers OnRequestGodlikePowers;
106 public event GodKickUser OnGodKickUser;
107 public event ObjectExtraParams OnUpdateExtraParams;
108 public event UpdateShape OnUpdatePrimShape;
109 public event ObjectRequest OnObjectRequest;
110 public event ObjectSelect OnObjectSelect;
111 public event ObjectDeselect OnObjectDeselect;
112 public event GenericCall7 OnObjectDescription;
113 public event GenericCall7 OnObjectName;
114 public event GenericCall7 OnObjectClickAction;
115 public event GenericCall7 OnObjectMaterial;
116 public event ObjectIncludeInSearch OnObjectIncludeInSearch;
117 public event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily;
118 public event UpdatePrimFlags OnUpdatePrimFlags;
119 public event UpdatePrimTexture OnUpdatePrimTexture;
120 public event UpdateVector OnUpdatePrimGroupPosition;
121 public event UpdateVector OnUpdatePrimSinglePosition;
122 public event UpdatePrimRotation OnUpdatePrimGroupRotation;
123 public event UpdatePrimSingleRotation OnUpdatePrimSingleRotation;
124 public event UpdatePrimSingleRotationPosition OnUpdatePrimSingleRotationPosition;
125 public event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation;
126 public event UpdateVector OnUpdatePrimScale;
127 public event UpdateVector OnUpdatePrimGroupScale;
128 public event StatusChange OnChildAgentStatus;
129 public event GenericCall2 OnStopMovement;
130 public event Action<UUID> OnRemoveAvatar;
131 public event RequestMapBlocks OnRequestMapBlocks;
132 public event RequestMapName OnMapNameRequest;
133 public event TeleportLocationRequest OnTeleportLocationRequest;
134 public event TeleportLandmarkRequest OnTeleportLandmarkRequest;
135 public event DisconnectUser OnDisconnectUser;
136 public event RequestAvatarProperties OnRequestAvatarProperties;
137 public event SetAlwaysRun OnSetAlwaysRun;
138 public event FetchInventory OnAgentDataUpdateRequest;
139 public event TeleportLocationRequest OnSetStartLocationRequest;
140 public event UpdateAvatarProperties OnUpdateAvatarProperties;
141 public event CreateNewInventoryItem OnCreateNewInventoryItem;
142 public event CreateInventoryFolder OnCreateNewInventoryFolder;
143 public event UpdateInventoryFolder OnUpdateInventoryFolder;
144 public event MoveInventoryFolder OnMoveInventoryFolder;
145 public event FetchInventoryDescendents OnFetchInventoryDescendents;
146 public event PurgeInventoryDescendents OnPurgeInventoryDescendents;
147 public event FetchInventory OnFetchInventory;
148 public event RequestTaskInventory OnRequestTaskInventory;
149 public event UpdateInventoryItem OnUpdateInventoryItem;
150 public event CopyInventoryItem OnCopyInventoryItem;
151 public event MoveInventoryItem OnMoveInventoryItem;
152 public event RemoveInventoryItem OnRemoveInventoryItem;
153 public event RemoveInventoryFolder OnRemoveInventoryFolder;
154 public event UDPAssetUploadRequest OnAssetUploadRequest;
155 public event XferReceive OnXferReceive;
156 public event RequestXfer OnRequestXfer;
157 public event ConfirmXfer OnConfirmXfer;
158 public event AbortXfer OnAbortXfer;
159 public event RequestTerrain OnRequestTerrain;
160 public event RezScript OnRezScript;
161 public event UpdateTaskInventory OnUpdateTaskInventory;
162 public event MoveTaskInventory OnMoveTaskItem;
163 public event RemoveTaskInventory OnRemoveTaskItem;
164 public event RequestAsset OnRequestAsset;
165 public event UUIDNameRequest OnNameFromUUIDRequest;
166 public event ParcelAccessListRequest OnParcelAccessListRequest;
167 public event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest;
168 public event ParcelPropertiesRequest OnParcelPropertiesRequest;
169 public event ParcelDivideRequest OnParcelDivideRequest;
170 public event ParcelJoinRequest OnParcelJoinRequest;
171 public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest;
172 public event ParcelSelectObjects OnParcelSelectObjects;
173 public event ParcelObjectOwnerRequest OnParcelObjectOwnerRequest;
174 public event ParcelAbandonRequest OnParcelAbandonRequest;
175 public event ParcelGodForceOwner OnParcelGodForceOwner;
176 public event ParcelReclaim OnParcelReclaim;
177 public event ParcelReturnObjectsRequest OnParcelReturnObjectsRequest;
178 public event ParcelDeedToGroup OnParcelDeedToGroup;
179 public event RegionInfoRequest OnRegionInfoRequest;
180 public event EstateCovenantRequest OnEstateCovenantRequest;
181 public event FriendActionDelegate OnApproveFriendRequest;
182 public event FriendActionDelegate OnDenyFriendRequest;
183 public event FriendshipTermination OnTerminateFriendship;
184 public event MoneyTransferRequest OnMoneyTransferRequest;
185 public event EconomyDataRequest OnEconomyDataRequest;
186 public event MoneyBalanceRequest OnMoneyBalanceRequest;
187 public event ParcelBuy OnParcelBuy;
188 public event UUIDNameRequest OnTeleportHomeRequest;
189 public event UUIDNameRequest OnUUIDGroupNameRequest;
190 public event ScriptAnswer OnScriptAnswer;
191 public event RequestPayPrice OnRequestPayPrice;
192 public event ObjectSaleInfo OnObjectSaleInfo;
193 public event ObjectBuy OnObjectBuy;
194 public event BuyObjectInventory OnBuyObjectInventory;
195 public event AgentSit OnUndo;
196 public event ForceReleaseControls OnForceReleaseControls;
197 public event GodLandStatRequest OnLandStatRequest;
198 public event RequestObjectPropertiesFamily OnObjectGroupRequest;
199 public event DetailedEstateDataRequest OnDetailedEstateDataRequest;
200 public event SetEstateFlagsRequest OnSetEstateFlagsRequest;
201 public event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture;
202 public event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture;
203 public event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights;
204 public event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest;
205 public event SetRegionTerrainSettings OnSetRegionTerrainSettings;
206 public event BakeTerrain OnBakeTerrain;
207 public event RequestTerrain OnUploadTerrain;
208 public event EstateChangeInfo OnEstateChangeInfo;
209 public event EstateRestartSimRequest OnEstateRestartSimRequest;
210 public event EstateChangeCovenantRequest OnEstateChangeCovenantRequest;
211 public event UpdateEstateAccessDeltaRequest OnUpdateEstateAccessDeltaRequest;
212 public event SimulatorBlueBoxMessageRequest OnSimulatorBlueBoxMessageRequest;
213 public event EstateBlueBoxMessageRequest OnEstateBlueBoxMessageRequest;
214 public event EstateDebugRegionRequest OnEstateDebugRegionRequest;
215 public event EstateTeleportOneUserHomeRequest OnEstateTeleportOneUserHomeRequest;
216 public event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest;
217 public event RegionHandleRequest OnRegionHandleRequest;
218 public event ParcelInfoRequest OnParcelInfoRequest;
219 public event ScriptReset OnScriptReset;
220 public event GetScriptRunning OnGetScriptRunning;
221 public event SetScriptRunning OnSetScriptRunning;
222 public event UpdateVector OnAutoPilotGo;
223 public event TerrainUnacked OnUnackedTerrain;
224 public event ActivateGesture OnActivateGesture;
225 public event DeactivateGesture OnDeactivateGesture;
226 public event ObjectOwner OnObjectOwner;
227 public event DirPlacesQuery OnDirPlacesQuery;
228 public event DirFindQuery OnDirFindQuery;
229 public event DirLandQuery OnDirLandQuery;
230 public event DirPopularQuery OnDirPopularQuery;
231 public event DirClassifiedQuery OnDirClassifiedQuery;
232 public event EventInfoRequest OnEventInfoRequest;
233 public event ParcelSetOtherCleanTime OnParcelSetOtherCleanTime;
234 public event MapItemRequest OnMapItemRequest;
235 public event OfferCallingCard OnOfferCallingCard;
236 public event AcceptCallingCard OnAcceptCallingCard;
237 public event DeclineCallingCard OnDeclineCallingCard;
238 public event SoundTrigger OnSoundTrigger;
239 public event StartLure OnStartLure;
240 public event TeleportLureRequest OnTeleportLureRequest;
241 public event NetworkStats OnNetworkStatsUpdate;
242 public event ClassifiedInfoRequest OnClassifiedInfoRequest;
243 public event ClassifiedInfoUpdate OnClassifiedInfoUpdate;
244 public event ClassifiedDelete OnClassifiedDelete;
245 public event ClassifiedDelete OnClassifiedGodDelete;
246 public event EventNotificationAddRequest OnEventNotificationAddRequest;
247 public event EventNotificationRemoveRequest OnEventNotificationRemoveRequest;
248 public event EventGodDelete OnEventGodDelete;
249 public event ParcelDwellRequest OnParcelDwellRequest;
250 public event UserInfoRequest OnUserInfoRequest;
251 public event UpdateUserInfo OnUpdateUserInfo;
252 public event RetrieveInstantMessages OnRetrieveInstantMessages;
253 public event PickDelete OnPickDelete;
254 public event PickGodDelete OnPickGodDelete;
255 public event PickInfoUpdate OnPickInfoUpdate;
256 public event AvatarNotesUpdate OnAvatarNotesUpdate;
257 public event MuteListRequest OnMuteListRequest;
258 public event AvatarInterestUpdate OnAvatarInterestUpdate;
259 public event PlacesQuery OnPlacesQuery;
260
261 #endregion Events
262
263 #region Class Members
264
61 // LLClientView Only 265 // LLClientView Only
62 public delegate void BinaryGenericMessage(Object sender, string method, byte[][] args); 266 public delegate void BinaryGenericMessage(Object sender, string method, byte[][] args);
63 267
@@ -110,15 +314,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
110 protected int m_primFullUpdatesPerPacket = 14; 314 protected int m_primFullUpdatesPerPacket = 14;
111 protected int m_primTerseUpdateRate = 10; 315 protected int m_primTerseUpdateRate = 10;
112 protected int m_primFullUpdateRate = 14; 316 protected int m_primFullUpdateRate = 14;
113 protected int m_textureSendLimit = 20;
114 protected int m_textureDataLimit = 10;
115 protected int m_avatarTerseUpdateRate = 50; 317 protected int m_avatarTerseUpdateRate = 50;
116 protected int m_avatarTerseUpdatesPerPacket = 5; 318 protected int m_avatarTerseUpdatesPerPacket = 5;
117 protected int m_packetMTU = 1400; 319 /// <summary>Number of texture packets to put on the queue each time the
320 /// OnQueueEmpty event is triggered for the texture category</summary>
321 protected int m_textureSendLimit = 20;
118 protected IAssetService m_assetService; 322 protected IAssetService m_assetService;
323 private IHyperAssetService m_hyperAssets;
324
325
326 #endregion Class Members
119 327
120 #region Properties 328 #region Properties
121 329
330 public LLUDPClient UDPClient { get { return m_udpClient; } }
331 public IPEndPoint RemoteEndPoint { get { return m_udpClient.RemoteEndPoint; } }
122 public UUID SecureSessionId { get { return m_secureSessionId; } } 332 public UUID SecureSessionId { get { return m_secureSessionId; } }
123 public IScene Scene { get { return m_scene; } } 333 public IScene Scene { get { return m_scene; } }
124 public UUID SessionId { get { return m_sessionId; } } 334 public UUID SessionId { get { return m_sessionId; } }
@@ -170,6 +380,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
170 380
171 m_scene = scene; 381 m_scene = scene;
172 m_assetService = m_scene.RequestModuleInterface<IAssetService>(); 382 m_assetService = m_scene.RequestModuleInterface<IAssetService>();
383 m_hyperAssets = m_scene.RequestModuleInterface<IHyperAssetService>();
173 m_GroupsModule = scene.RequestModuleInterface<IGroupsModule>(); 384 m_GroupsModule = scene.RequestModuleInterface<IGroupsModule>();
174 m_imageManager = new LLImageManager(this, m_assetService, Scene.RequestModuleInterface<IJ2KDecoder>()); 385 m_imageManager = new LLImageManager(this, m_assetService, Scene.RequestModuleInterface<IJ2KDecoder>());
175 m_channelVersion = Utils.StringToBytes(scene.GetSimulatorVersion()); 386 m_channelVersion = Utils.StringToBytes(scene.GetSimulatorVersion());
@@ -198,51 +409,35 @@ namespace OpenSim.Region.ClientStack.LindenUDP
198 #region Client Methods 409 #region Client Methods
199 410
200 /// <summary> 411 /// <summary>
201 /// Close down the client view. This *must* be the last method called, since the last # 412 /// Shut down the client view
202 /// statement of CloseCleanup() aborts the thread.
203 /// </summary> 413 /// </summary>
204 /// <param name="shutdownCircuit"></param> 414 public void Close()
205 public void Close(bool shutdownCircuit)
206 { 415 {
207 m_log.DebugFormat( 416 m_log.DebugFormat(
208 "[CLIENT]: Close has been called with shutdownCircuit = {0} for {1} attached to scene {2}", 417 "[CLIENT]: Close has been called for {0} attached to scene {1}",
209 shutdownCircuit, Name, m_scene.RegionInfo.RegionName); 418 Name, m_scene.RegionInfo.RegionName);
419
420 // Send the STOP packet
421 DisableSimulatorPacket disable = (DisableSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.DisableSimulator);
422 OutPacket(disable, ThrottleOutPacketType.Unknown);
423
424 IsActive = false;
210 425
426 // Shutdown the image manager
211 if (m_imageManager != null) 427 if (m_imageManager != null)
212 m_imageManager.Close(); 428 m_imageManager.Close();
213 429
430 // Fire the callback for this connection closing
431 if (OnConnectionClosed != null)
432 OnConnectionClosed(this);
433
434 // Flush all of the packets out of the UDP server for this client
214 if (m_udpServer != null) 435 if (m_udpServer != null)
215 m_udpServer.Flush(); 436 m_udpServer.Flush(m_udpClient);
216
217 // raise an event on the packet server to Shutdown the circuit
218 // Now, if we raise the event then the packet server will call this method itself, so don't try cleanup
219 // here otherwise we'll end up calling it twice.
220 // FIXME: In truth, I might be wrong but this whole business of calling this method twice (with different args) looks
221 // horribly tangly. Hopefully it should be possible to greatly simplify it.
222 if (shutdownCircuit)
223 {
224 if (OnConnectionClosed != null)
225 OnConnectionClosed(this);
226 }
227 else
228 {
229 CloseCleanup(shutdownCircuit);
230 }
231 }
232 437
233 private void CloseCleanup(bool shutdownCircuit) 438 // Remove ourselves from the scene
234 {
235 m_scene.RemoveClient(AgentId); 439 m_scene.RemoveClient(AgentId);
236 440
237 //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false));
238 //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true));
239
240 // Send the STOP packet
241 DisableSimulatorPacket disable = (DisableSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.DisableSimulator);
242 OutPacket(disable, ThrottleOutPacketType.Unknown);
243
244 Thread.Sleep(2000);
245
246 // Shut down timers. Thread Context of this method is murky. Lock all timers 441 // Shut down timers. Thread Context of this method is murky. Lock all timers
247 if (m_avatarTerseUpdateTimer.Enabled) 442 if (m_avatarTerseUpdateTimer.Enabled)
248 lock (m_avatarTerseUpdateTimer) 443 lock (m_avatarTerseUpdateTimer)
@@ -254,43 +449,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
254 lock (m_primFullUpdateTimer) 449 lock (m_primFullUpdateTimer)
255 m_primFullUpdateTimer.Stop(); 450 m_primFullUpdateTimer.Stop();
256 451
257 // This is just to give the client a reasonable chance of
258 // flushing out all it's packets. There should probably
259 // be a better mechanism here
260
261 // We can't reach into other scenes and close the connection 452 // We can't reach into other scenes and close the connection
262 // We need to do this over grid communications 453 // We need to do this over grid communications
263 //m_scene.CloseAllAgents(CircuitCode); 454 //m_scene.CloseAllAgents(CircuitCode);
264 455
265 // If we're not shutting down the circuit, then this is the last time we'll go here. 456 m_avatarTerseUpdateTimer.Dispose();
266 // If we are shutting down the circuit, the UDP Server will come back here with 457 m_primTerseUpdateTimer.Dispose();
267 // ShutDownCircuit = false 458 m_primFullUpdateTimer.Dispose();
268 if (!(shutdownCircuit))
269 {
270 GC.Collect();
271 m_imageManager = null;
272 // Sends a KillPacket object, with which, the
273 // blockingqueue dequeues and sees it's a killpacket
274 // and terminates within the context of the client thread.
275 // This ensures that it's done from within the context
276 // of the client thread regardless of where Close() is called.
277 KillEndDone();
278 }
279
280 IsActive = false;
281
282 m_avatarTerseUpdateTimer.Close();
283 m_primTerseUpdateTimer.Close();
284 m_primFullUpdateTimer.Close();
285 459
286 //m_udpServer.OnPacketStats -= PopulateStats; 460 // Disable UDP handling for this client
287 m_udpClient.Shutdown(); 461 m_udpClient.Shutdown();
288 462
289 // wait for thread stoped 463 //m_log.InfoFormat("[CLIENTVIEW] Memory pre GC {0}", System.GC.GetTotalMemory(false));
290 // m_clientThread.Join(); 464 //GC.Collect();
291 465 //m_log.InfoFormat("[CLIENTVIEW] Memory post GC {0}", System.GC.GetTotalMemory(true));
292 // delete circuit code
293 //m_networkServer.CloseClient(this);
294 } 466 }
295 467
296 public void Kick(string message) 468 public void Kick(string message)
@@ -412,43 +584,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
412 return result; 584 return result;
413 } 585 }
414 586
415 /*protected void DebugPacket(string direction, Packet packet)
416 {
417 string info;
418
419 if (m_debugPacketLevel < 255 && packet.Type == PacketType.AgentUpdate)
420 return;
421 if (m_debugPacketLevel < 254 && packet.Type == PacketType.ViewerEffect)
422 return;
423 if (m_debugPacketLevel < 253 && (
424 packet.Type == PacketType.CompletePingCheck ||
425 packet.Type == PacketType.StartPingCheck
426 ))
427 return;
428 if (m_debugPacketLevel < 252 && packet.Type == PacketType.PacketAck)
429 return;
430
431 if (m_debugPacketLevel > 1)
432 {
433 info = packet.ToString();
434 }
435 else
436 {
437 info = packet.Type.ToString();
438 }
439
440 Console.WriteLine(m_circuitCode + ":" + direction + ": " + info);
441 }*/
442
443 #endregion Packet Handling 587 #endregion Packet Handling
444 588
445 # region Setup 589 # region Setup
446 590
447 /// <summary> 591 public virtual void Start()
448 /// Starts up the timers to check the client and resend unacked packets
449 /// Adds the client to the OpenSim.Region.Framework.Scenes.Scene
450 /// </summary>
451 protected virtual void InitNewClient()
452 { 592 {
453 m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate); 593 m_avatarTerseUpdateTimer = new Timer(m_avatarTerseUpdateRate);
454 m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates); 594 m_avatarTerseUpdateTimer.Elapsed += new ElapsedEventHandler(ProcessAvatarTerseUpdates);
@@ -467,262 +607,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
467 RefreshGroupMembership(); 607 RefreshGroupMembership();
468 } 608 }
469 609
470 public virtual void Start()
471 {
472 // This sets up all the timers
473 InitNewClient();
474 }
475
476 /// <summary>
477 /// Run a user session. This method lies at the base of the entire client thread.
478 /// </summary>
479 protected void RunUserSession()
480 {
481 try
482 {
483
484 }
485 catch (Exception e)
486 {
487 if (e is ThreadAbortException)
488 throw;
489
490 if (StatsManager.SimExtraStats != null)
491 StatsManager.SimExtraStats.AddAbnormalClientThreadTermination();
492
493 // Don't let a failure in an individual client thread crash the whole sim.
494 m_log.ErrorFormat(
495 "[CLIENT]: Client thread for {0} {1} crashed. Logging them out.", Name, AgentId);
496 m_log.Error(e.ToString());
497
498 try
499 {
500 // Make an attempt to alert the user that their session has crashed
501 AgentAlertMessagePacket packet
502 = BuildAgentAlertPacket(
503 "Unfortunately the session for this client on the server has crashed.\n"
504 + "Any further actions taken will not be processed.\n"
505 + "Please relog", true);
506
507 m_udpServer.SendPacket(m_agentId, packet, ThrottleOutPacketType.Unknown, false);
508
509 // There may be a better way to do this. Perhaps kick? Not sure this propogates notifications to
510 // listeners yet, though.
511 Logout(this);
512 }
513 catch (Exception e2)
514 {
515 if (e2 is ThreadAbortException)
516 throw;
517
518 m_log.ErrorFormat("[CLIENT]: Further exception thrown on forced session logout. {0}", e2);
519 }
520 }
521 }
522
523 # endregion 610 # endregion
524 611
525 #region Events
526
527 public event GenericMessage OnGenericMessage;
528 public event BinaryGenericMessage OnBinaryGenericMessage;
529 public event Action<IClientAPI> OnLogout;
530 public event ObjectPermissions OnObjectPermissions;
531 public event Action<IClientAPI> OnConnectionClosed;
532 public event ViewerEffectEventHandler OnViewerEffect;
533 public event ImprovedInstantMessage OnInstantMessage;
534 public event ChatMessage OnChatFromClient;
535 public event TextureRequest OnRequestTexture;
536 public event RezObject OnRezObject;
537 public event DeRezObject OnDeRezObject;
538 public event ModifyTerrain OnModifyTerrain;
539 public event Action<IClientAPI> OnRegionHandShakeReply;
540 public event GenericCall2 OnRequestWearables;
541 public event SetAppearance OnSetAppearance;
542 public event AvatarNowWearing OnAvatarNowWearing;
543 public event RezSingleAttachmentFromInv OnRezSingleAttachmentFromInv;
544 public event RezMultipleAttachmentsFromInv OnRezMultipleAttachmentsFromInv;
545 public event UUIDNameRequest OnDetachAttachmentIntoInv;
546 public event ObjectAttach OnObjectAttach;
547 public event ObjectDeselect OnObjectDetach;
548 public event ObjectDrop OnObjectDrop;
549 public event GenericCall2 OnCompleteMovementToRegion;
550 public event UpdateAgent OnAgentUpdate;
551 public event AgentRequestSit OnAgentRequestSit;
552 public event AgentSit OnAgentSit;
553 public event AvatarPickerRequest OnAvatarPickerRequest;
554 public event StartAnim OnStartAnim;
555 public event StopAnim OnStopAnim;
556 public event Action<IClientAPI> OnRequestAvatarsData;
557 public event LinkObjects OnLinkObjects;
558 public event DelinkObjects OnDelinkObjects;
559 public event GrabObject OnGrabObject;
560 public event DeGrabObject OnDeGrabObject;
561 public event SpinStart OnSpinStart;
562 public event SpinStop OnSpinStop;
563 public event ObjectDuplicate OnObjectDuplicate;
564 public event ObjectDuplicateOnRay OnObjectDuplicateOnRay;
565 public event MoveObject OnGrabUpdate;
566 public event SpinObject OnSpinUpdate;
567 public event AddNewPrim OnAddPrim;
568 public event RequestGodlikePowers OnRequestGodlikePowers;
569 public event GodKickUser OnGodKickUser;
570 public event ObjectExtraParams OnUpdateExtraParams;
571 public event UpdateShape OnUpdatePrimShape;
572 public event ObjectRequest OnObjectRequest;
573 public event ObjectSelect OnObjectSelect;
574 public event ObjectDeselect OnObjectDeselect;
575 public event GenericCall7 OnObjectDescription;
576 public event GenericCall7 OnObjectName;
577 public event GenericCall7 OnObjectClickAction;
578 public event GenericCall7 OnObjectMaterial;
579 public event ObjectIncludeInSearch OnObjectIncludeInSearch;
580 public event RequestObjectPropertiesFamily OnRequestObjectPropertiesFamily;
581 public event UpdatePrimFlags OnUpdatePrimFlags;
582 public event UpdatePrimTexture OnUpdatePrimTexture;
583 public event UpdateVector OnUpdatePrimGroupPosition;
584 public event UpdateVector OnUpdatePrimSinglePosition;
585 public event UpdatePrimRotation OnUpdatePrimGroupRotation;
586 public event UpdatePrimSingleRotation OnUpdatePrimSingleRotation;
587 public event UpdatePrimSingleRotationPosition OnUpdatePrimSingleRotationPosition;
588 public event UpdatePrimGroupRotation OnUpdatePrimGroupMouseRotation;
589 public event UpdateVector OnUpdatePrimScale;
590 public event UpdateVector OnUpdatePrimGroupScale;
591 public event StatusChange OnChildAgentStatus;
592 public event GenericCall2 OnStopMovement;
593 public event Action<UUID> OnRemoveAvatar;
594 public event RequestMapBlocks OnRequestMapBlocks;
595 public event RequestMapName OnMapNameRequest;
596 public event TeleportLocationRequest OnTeleportLocationRequest;
597 public event TeleportLandmarkRequest OnTeleportLandmarkRequest;
598 public event DisconnectUser OnDisconnectUser;
599 public event RequestAvatarProperties OnRequestAvatarProperties;
600 public event SetAlwaysRun OnSetAlwaysRun;
601 public event FetchInventory OnAgentDataUpdateRequest;
602 public event TeleportLocationRequest OnSetStartLocationRequest;
603 public event UpdateAvatarProperties OnUpdateAvatarProperties;
604 public event CreateNewInventoryItem OnCreateNewInventoryItem;
605 public event CreateInventoryFolder OnCreateNewInventoryFolder;
606 public event UpdateInventoryFolder OnUpdateInventoryFolder;
607 public event MoveInventoryFolder OnMoveInventoryFolder;
608 public event FetchInventoryDescendents OnFetchInventoryDescendents;
609 public event PurgeInventoryDescendents OnPurgeInventoryDescendents;
610 public event FetchInventory OnFetchInventory;
611 public event RequestTaskInventory OnRequestTaskInventory;
612 public event UpdateInventoryItem OnUpdateInventoryItem;
613 public event CopyInventoryItem OnCopyInventoryItem;
614 public event MoveInventoryItem OnMoveInventoryItem;
615 public event RemoveInventoryItem OnRemoveInventoryItem;
616 public event RemoveInventoryFolder OnRemoveInventoryFolder;
617 public event UDPAssetUploadRequest OnAssetUploadRequest;
618 public event XferReceive OnXferReceive;
619 public event RequestXfer OnRequestXfer;
620 public event ConfirmXfer OnConfirmXfer;
621 public event AbortXfer OnAbortXfer;
622 public event RequestTerrain OnRequestTerrain;
623 public event RezScript OnRezScript;
624 public event UpdateTaskInventory OnUpdateTaskInventory;
625 public event MoveTaskInventory OnMoveTaskItem;
626 public event RemoveTaskInventory OnRemoveTaskItem;
627 public event RequestAsset OnRequestAsset;
628 public event UUIDNameRequest OnNameFromUUIDRequest;
629 public event ParcelAccessListRequest OnParcelAccessListRequest;
630 public event ParcelAccessListUpdateRequest OnParcelAccessListUpdateRequest;
631 public event ParcelPropertiesRequest OnParcelPropertiesRequest;
632 public event ParcelDivideRequest OnParcelDivideRequest;
633 public event ParcelJoinRequest OnParcelJoinRequest;
634 public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest;
635 public event ParcelSelectObjects OnParcelSelectObjects;
636 public event ParcelObjectOwnerRequest OnParcelObjectOwnerRequest;
637 public event ParcelAbandonRequest OnParcelAbandonRequest;
638 public event ParcelGodForceOwner OnParcelGodForceOwner;
639 public event ParcelReclaim OnParcelReclaim;
640 public event ParcelReturnObjectsRequest OnParcelReturnObjectsRequest;
641 public event ParcelDeedToGroup OnParcelDeedToGroup;
642 public event RegionInfoRequest OnRegionInfoRequest;
643 public event EstateCovenantRequest OnEstateCovenantRequest;
644 public event FriendActionDelegate OnApproveFriendRequest;
645 public event FriendActionDelegate OnDenyFriendRequest;
646 public event FriendshipTermination OnTerminateFriendship;
647 public event MoneyTransferRequest OnMoneyTransferRequest;
648 public event EconomyDataRequest OnEconomyDataRequest;
649 public event MoneyBalanceRequest OnMoneyBalanceRequest;
650 public event ParcelBuy OnParcelBuy;
651 public event UUIDNameRequest OnTeleportHomeRequest;
652 public event UUIDNameRequest OnUUIDGroupNameRequest;
653 public event ScriptAnswer OnScriptAnswer;
654 public event RequestPayPrice OnRequestPayPrice;
655 public event ObjectSaleInfo OnObjectSaleInfo;
656 public event ObjectBuy OnObjectBuy;
657 public event BuyObjectInventory OnBuyObjectInventory;
658 public event AgentSit OnUndo;
659 public event ForceReleaseControls OnForceReleaseControls;
660 public event GodLandStatRequest OnLandStatRequest;
661 public event RequestObjectPropertiesFamily OnObjectGroupRequest;
662 public event DetailedEstateDataRequest OnDetailedEstateDataRequest;
663 public event SetEstateFlagsRequest OnSetEstateFlagsRequest;
664 public event SetEstateTerrainBaseTexture OnSetEstateTerrainBaseTexture;
665 public event SetEstateTerrainDetailTexture OnSetEstateTerrainDetailTexture;
666 public event SetEstateTerrainTextureHeights OnSetEstateTerrainTextureHeights;
667 public event CommitEstateTerrainTextureRequest OnCommitEstateTerrainTextureRequest;
668 public event SetRegionTerrainSettings OnSetRegionTerrainSettings;
669 public event BakeTerrain OnBakeTerrain;
670 public event RequestTerrain OnUploadTerrain;
671 public event EstateChangeInfo OnEstateChangeInfo;
672 public event EstateRestartSimRequest OnEstateRestartSimRequest;
673 public event EstateChangeCovenantRequest OnEstateChangeCovenantRequest;
674 public event UpdateEstateAccessDeltaRequest OnUpdateEstateAccessDeltaRequest;
675 public event SimulatorBlueBoxMessageRequest OnSimulatorBlueBoxMessageRequest;
676 public event EstateBlueBoxMessageRequest OnEstateBlueBoxMessageRequest;
677 public event EstateDebugRegionRequest OnEstateDebugRegionRequest;
678 public event EstateTeleportOneUserHomeRequest OnEstateTeleportOneUserHomeRequest;
679 public event EstateTeleportAllUsersHomeRequest OnEstateTeleportAllUsersHomeRequest;
680 public event RegionHandleRequest OnRegionHandleRequest;
681 public event ParcelInfoRequest OnParcelInfoRequest;
682 public event ScriptReset OnScriptReset;
683 public event GetScriptRunning OnGetScriptRunning;
684 public event SetScriptRunning OnSetScriptRunning;
685 public event UpdateVector OnAutoPilotGo;
686 public event TerrainUnacked OnUnackedTerrain;
687 public event ActivateGesture OnActivateGesture;
688 public event DeactivateGesture OnDeactivateGesture;
689 public event ObjectOwner OnObjectOwner;
690 public event DirPlacesQuery OnDirPlacesQuery;
691 public event DirFindQuery OnDirFindQuery;
692 public event DirLandQuery OnDirLandQuery;
693 public event DirPopularQuery OnDirPopularQuery;
694 public event DirClassifiedQuery OnDirClassifiedQuery;
695 public event EventInfoRequest OnEventInfoRequest;
696 public event ParcelSetOtherCleanTime OnParcelSetOtherCleanTime;
697 public event MapItemRequest OnMapItemRequest;
698 public event OfferCallingCard OnOfferCallingCard;
699 public event AcceptCallingCard OnAcceptCallingCard;
700 public event DeclineCallingCard OnDeclineCallingCard;
701 public event SoundTrigger OnSoundTrigger;
702 public event StartLure OnStartLure;
703 public event TeleportLureRequest OnTeleportLureRequest;
704 public event NetworkStats OnNetworkStatsUpdate;
705 public event ClassifiedInfoRequest OnClassifiedInfoRequest;
706 public event ClassifiedInfoUpdate OnClassifiedInfoUpdate;
707 public event ClassifiedDelete OnClassifiedDelete;
708 public event ClassifiedDelete OnClassifiedGodDelete;
709 public event EventNotificationAddRequest OnEventNotificationAddRequest;
710 public event EventNotificationRemoveRequest OnEventNotificationRemoveRequest;
711 public event EventGodDelete OnEventGodDelete;
712 public event ParcelDwellRequest OnParcelDwellRequest;
713 public event UserInfoRequest OnUserInfoRequest;
714 public event UpdateUserInfo OnUpdateUserInfo;
715 public event RetrieveInstantMessages OnRetrieveInstantMessages;
716 public event PickDelete OnPickDelete;
717 public event PickGodDelete OnPickGodDelete;
718 public event PickInfoUpdate OnPickInfoUpdate;
719 public event AvatarNotesUpdate OnAvatarNotesUpdate;
720 public event MuteListRequest OnMuteListRequest;
721 public event AvatarInterestUpdate OnAvatarInterestUpdate;
722 public event PlacesQuery OnPlacesQuery;
723
724 #endregion Events
725
726 public void ActivateGesture(UUID assetId, UUID gestureId) 612 public void ActivateGesture(UUID assetId, UUID gestureId)
727 { 613 {
728 } 614 }
@@ -1342,7 +1228,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1342 kill.ObjectData[0].ID = localID; 1228 kill.ObjectData[0].ID = localID;
1343 kill.Header.Reliable = true; 1229 kill.Header.Reliable = true;
1344 kill.Header.Zerocoded = true; 1230 kill.Header.Zerocoded = true;
1345 OutPacket(kill, ThrottleOutPacketType.Task); 1231 OutPacket(kill, ThrottleOutPacketType.State);
1346 } 1232 }
1347 1233
1348 /// <summary> 1234 /// <summary>
@@ -1936,7 +1822,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1936 sendXfer.XferID.ID = xferID; 1822 sendXfer.XferID.ID = xferID;
1937 sendXfer.XferID.Packet = packet; 1823 sendXfer.XferID.Packet = packet;
1938 sendXfer.DataPacket.Data = data; 1824 sendXfer.DataPacket.Data = data;
1939 OutPacket(sendXfer, ThrottleOutPacketType.Task); 1825 OutPacket(sendXfer, ThrottleOutPacketType.Asset);
1940 } 1826 }
1941 1827
1942 public void SendEconomyData(float EnergyEfficiency, int ObjectCapacity, int ObjectCount, int PriceEnergyUnit, 1828 public void SendEconomyData(float EnergyEfficiency, int ObjectCapacity, int ObjectCount, int PriceEnergyUnit,
@@ -2218,7 +2104,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2218 packet.AgentData.SessionID = SessionId; 2104 packet.AgentData.SessionID = SessionId;
2219 packet.Header.Reliable = false; 2105 packet.Header.Reliable = false;
2220 packet.Header.Zerocoded = true; 2106 packet.Header.Zerocoded = true;
2221 OutPacket(packet, ThrottleOutPacketType.Task); 2107 OutPacket(packet, ThrottleOutPacketType.State);
2222 } 2108 }
2223 2109
2224 public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember, 2110 public void SendAvatarProperties(UUID avatarID, string aboutText, string bornOn, Byte[] charterMember,
@@ -3241,7 +3127,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3241 3127
3242 avp.Sender.IsTrial = false; 3128 avp.Sender.IsTrial = false;
3243 avp.Sender.ID = agentID; 3129 avp.Sender.ID = agentID;
3244 OutPacket(avp, ThrottleOutPacketType.Task); 3130 OutPacket(avp, ThrottleOutPacketType.State);
3245 } 3131 }
3246 3132
3247 public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) 3133 public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs)
@@ -3366,7 +3252,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3366 int length = 0; 3252 int length = 0;
3367 m_avatarTerseUpdates[count].ToBytes(blockbuffer, ref length); 3253 m_avatarTerseUpdates[count].ToBytes(blockbuffer, ref length);
3368 length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); 3254 length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer);
3369 if (size + length > m_packetMTU) 3255 if (size + length > Packet.MTU)
3370 break; 3256 break;
3371 size += length; 3257 size += length;
3372 } 3258 }
@@ -3381,6 +3267,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3381 3267
3382 terse.Header.Reliable = false; 3268 terse.Header.Reliable = false;
3383 terse.Header.Zerocoded = true; 3269 terse.Header.Zerocoded = true;
3270 // FIXME: Move this to ThrottleOutPacketType.State when the real prioritization code is committed
3384 OutPacket(terse, ThrottleOutPacketType.Task); 3271 OutPacket(terse, ThrottleOutPacketType.Task);
3385 3272
3386 if (m_avatarTerseUpdates.Count == 0) 3273 if (m_avatarTerseUpdates.Count == 0)
@@ -3571,7 +3458,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3571 void ProcessTextureRequests() 3458 void ProcessTextureRequests()
3572 { 3459 {
3573 if (m_imageManager != null) 3460 if (m_imageManager != null)
3574 m_imageManager.ProcessImageQueue(m_textureSendLimit, m_textureDataLimit); 3461 m_imageManager.ProcessImageQueue(m_textureSendLimit);
3575 } 3462 }
3576 3463
3577 void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e) 3464 void ProcessPrimFullUpdates(object sender, ElapsedEventArgs e)
@@ -3610,7 +3497,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3610 int length = 0; 3497 int length = 0;
3611 m_primFullUpdates[count].ToBytes(blockbuffer, ref length); 3498 m_primFullUpdates[count].ToBytes(blockbuffer, ref length);
3612 length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); 3499 length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer);
3613 if (size + length > m_packetMTU) 3500 if (size + length > Packet.MTU)
3614 break; 3501 break;
3615 size += length; 3502 size += length;
3616 } 3503 }
@@ -3625,7 +3512,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3625 } 3512 }
3626 3513
3627 outPacket.Header.Zerocoded = true; 3514 outPacket.Header.Zerocoded = true;
3628 OutPacket(outPacket, ThrottleOutPacketType.Task); 3515 OutPacket(outPacket, ThrottleOutPacketType.State);
3629 3516
3630 if (m_primFullUpdates.Count == 0 && m_primFullUpdateTimer.Enabled) 3517 if (m_primFullUpdates.Count == 0 && m_primFullUpdateTimer.Enabled)
3631 lock (m_primFullUpdateTimer) 3518 lock (m_primFullUpdateTimer)
@@ -3698,7 +3585,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3698 int length = 0; 3585 int length = 0;
3699 m_primTerseUpdates[count].ToBytes(blockbuffer, ref length); 3586 m_primTerseUpdates[count].ToBytes(blockbuffer, ref length);
3700 length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer); 3587 length = Helpers.ZeroEncode(blockbuffer, length, zerobuffer);
3701 if (size + length > m_packetMTU) 3588 if (size + length > Packet.MTU)
3702 break; 3589 break;
3703 size += length; 3590 size += length;
3704 } 3591 }
@@ -3715,7 +3602,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3715 3602
3716 outPacket.Header.Reliable = false; 3603 outPacket.Header.Reliable = false;
3717 outPacket.Header.Zerocoded = true; 3604 outPacket.Header.Zerocoded = true;
3718 OutPacket(outPacket, ThrottleOutPacketType.Task); 3605 OutPacket(outPacket, ThrottleOutPacketType.State);
3719 3606
3720 if (m_primTerseUpdates.Count == 0) 3607 if (m_primTerseUpdates.Count == 0)
3721 lock (m_primTerseUpdateTimer) 3608 lock (m_primTerseUpdateTimer)
@@ -4801,7 +4688,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4801 4688
4802 public bool HandleObjectGroupRequest(IClientAPI sender, Packet Pack) 4689 public bool HandleObjectGroupRequest(IClientAPI sender, Packet Pack)
4803 { 4690 {
4804
4805 ObjectGroupPacket ogpack = (ObjectGroupPacket)Pack; 4691 ObjectGroupPacket ogpack = (ObjectGroupPacket)Pack;
4806 if (ogpack.AgentData.SessionID != SessionId) return false; 4692 if (ogpack.AgentData.SessionID != SessionId) return false;
4807 4693
@@ -7013,7 +6899,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
7013 #endregion 6899 #endregion
7014 6900
7015 //handlerTextureRequest = null; 6901 //handlerTextureRequest = null;
7016
7017 for (int i = 0; i < imageRequest.RequestImage.Length; i++) 6902 for (int i = 0; i < imageRequest.RequestImage.Length; i++)
7018 { 6903 {
7019 if (OnRequestTexture != null) 6904 if (OnRequestTexture != null)
@@ -7024,7 +6909,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
7024 args.PacketNumber = imageRequest.RequestImage[i].Packet; 6909 args.PacketNumber = imageRequest.RequestImage[i].Packet;
7025 args.Priority = imageRequest.RequestImage[i].DownloadPriority; 6910 args.Priority = imageRequest.RequestImage[i].DownloadPriority;
7026 args.requestSequence = imageRequest.Header.Sequence; 6911 args.requestSequence = imageRequest.Header.Sequence;
7027
7028 //handlerTextureRequest = OnRequestTexture; 6912 //handlerTextureRequest = OnRequestTexture;
7029 6913
7030 //if (handlerTextureRequest != null) 6914 //if (handlerTextureRequest != null)
@@ -7047,10 +6931,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
7047 // Validate inventory transfers 6931 // Validate inventory transfers
7048 // Has to be done here, because AssetCache can't do it 6932 // Has to be done here, because AssetCache can't do it
7049 // 6933 //
7050 6934 UUID taskID = UUID.Zero;
7051 if (transfer.TransferInfo.SourceType == 3) 6935 if (transfer.TransferInfo.SourceType == 3)
7052 { 6936 {
7053 UUID taskID = new UUID(transfer.TransferInfo.Params, 48); 6937 taskID = new UUID(transfer.TransferInfo.Params, 48);
7054 UUID itemID = new UUID(transfer.TransferInfo.Params, 64); 6938 UUID itemID = new UUID(transfer.TransferInfo.Params, 64);
7055 UUID requestID = new UUID(transfer.TransferInfo.Params, 80); 6939 UUID requestID = new UUID(transfer.TransferInfo.Params, 80);
7056 if (!(((Scene)m_scene).Permissions.BypassPermissions())) 6940 if (!(((Scene)m_scene).Permissions.BypassPermissions()))
@@ -7121,7 +7005,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
7121 7005
7122 //m_assetCache.AddAssetRequest(this, transfer); 7006 //m_assetCache.AddAssetRequest(this, transfer);
7123 7007
7124 MakeAssetRequest(transfer); 7008 MakeAssetRequest(transfer, taskID);
7125 7009
7126 /* RequestAsset = OnRequestAsset; 7010 /* RequestAsset = OnRequestAsset;
7127 if (RequestAsset != null) 7011 if (RequestAsset != null)
@@ -10250,7 +10134,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
10250 10134
10251 public void KillEndDone() 10135 public void KillEndDone()
10252 { 10136 {
10253 m_udpClient.Shutdown();
10254 } 10137 }
10255 10138
10256 #region IClientCore 10139 #region IClientCore
@@ -10293,15 +10176,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
10293 { 10176 {
10294 Kick(reason); 10177 Kick(reason);
10295 Thread.Sleep(1000); 10178 Thread.Sleep(1000);
10296 Close(true); 10179 Close();
10297 } 10180 }
10298 10181
10299 public void Disconnect() 10182 public void Disconnect()
10300 { 10183 {
10301 Close(true); 10184 Close();
10302 } 10185 }
10303 10186
10304
10305 #endregion 10187 #endregion
10306 10188
10307 public void RefreshGroupMembership() 10189 public void RefreshGroupMembership()
@@ -10330,7 +10212,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
10330 return String.Empty; 10212 return String.Empty;
10331 } 10213 }
10332 10214
10333 public void MakeAssetRequest(TransferRequestPacket transferRequest) 10215 public void MakeAssetRequest(TransferRequestPacket transferRequest, UUID taskID)
10334 { 10216 {
10335 UUID requestID = UUID.Zero; 10217 UUID requestID = UUID.Zero;
10336 if (transferRequest.TransferInfo.SourceType == 2) 10218 if (transferRequest.TransferInfo.SourceType == 2)
@@ -10342,12 +10224,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP
10342 { 10224 {
10343 //inventory asset request 10225 //inventory asset request
10344 requestID = new UUID(transferRequest.TransferInfo.Params, 80); 10226 requestID = new UUID(transferRequest.TransferInfo.Params, 80);
10345 //m_log.Debug("asset request " + requestID); 10227 //m_log.Debug("[XXX] inventory asset request " + requestID);
10228 //if (taskID == UUID.Zero) // Agent
10229 // if (m_scene is HGScene)
10230 // {
10231 // m_log.Debug("[XXX] hg asset request " + requestID);
10232 // // We may need to fetch the asset from the user's asset server into the local asset server
10233 // HGAssetMapper mapper = ((HGScene)m_scene).AssetMapper;
10234 // mapper.Get(requestID, AgentId);
10235 // }
10346 } 10236 }
10347 10237
10348 //check to see if asset is in local cache, if not we need to request it from asset server. 10238 //check to see if asset is in local cache, if not we need to request it from asset server.
10349 //m_log.Debug("asset request " + requestID); 10239 //m_log.Debug("asset request " + requestID);
10350 10240
10351 m_assetService.Get(requestID.ToString(), transferRequest, AssetReceived); 10241 m_assetService.Get(requestID.ToString(), transferRequest, AssetReceived);
10352 10242
10353 } 10243 }
@@ -10358,12 +10248,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
10358 10248
10359 UUID requestID = UUID.Zero; 10249 UUID requestID = UUID.Zero;
10360 byte source = 2; 10250 byte source = 2;
10361 if (transferRequest.TransferInfo.SourceType == 2) 10251 if ((transferRequest.TransferInfo.SourceType == 2) || (transferRequest.TransferInfo.SourceType == 2222))
10362 { 10252 {
10363 //direct asset request 10253 //direct asset request
10364 requestID = new UUID(transferRequest.TransferInfo.Params, 0); 10254 requestID = new UUID(transferRequest.TransferInfo.Params, 0);
10365 } 10255 }
10366 else if (transferRequest.TransferInfo.SourceType == 3) 10256 else if ((transferRequest.TransferInfo.SourceType == 3) || (transferRequest.TransferInfo.SourceType == 3333))
10367 { 10257 {
10368 //inventory asset request 10258 //inventory asset request
10369 requestID = new UUID(transferRequest.TransferInfo.Params, 80); 10259 requestID = new UUID(transferRequest.TransferInfo.Params, 80);
@@ -10371,10 +10261,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
10371 //m_log.Debug("asset request " + requestID); 10261 //m_log.Debug("asset request " + requestID);
10372 } 10262 }
10373 10263
10374 // FIXME: We never tell the client about assets which do not exist when requested by this transfer mechanism, which can't be right.
10375 if (null == asset) 10264 if (null == asset)
10376 { 10265 {
10266 if ((m_hyperAssets != null) && (transferRequest.TransferInfo.SourceType < 2000))
10267 {
10268 // Try the user's inventory, but only if it's different from the regions'
10269 string userAssets = m_hyperAssets.GetUserAssetServer(AgentId);
10270 if ((userAssets != string.Empty) && (userAssets != m_hyperAssets.GetSimAssetServer()))
10271 {
10272 m_log.DebugFormat("[CLIENT]: asset {0} not found in local asset storage. Trying user's storage.", id);
10273 if (transferRequest.TransferInfo.SourceType == 2)
10274 transferRequest.TransferInfo.SourceType = 2222; // marker
10275 else if (transferRequest.TransferInfo.SourceType == 3)
10276 transferRequest.TransferInfo.SourceType = 3333; // marker
10277
10278 m_assetService.Get(userAssets + "/" + id, transferRequest, AssetReceived);
10279 return;
10280 }
10281 }
10282
10377 //m_log.DebugFormat("[ASSET CACHE]: Asset transfer request for asset which is {0} already known to be missing. Dropping", requestID); 10283 //m_log.DebugFormat("[ASSET CACHE]: Asset transfer request for asset which is {0} already known to be missing. Dropping", requestID);
10284
10285 // FIXME: We never tell the client about assets which do not exist when requested by this transfer mechanism, which can't be right.
10378 return; 10286 return;
10379 } 10287 }
10380 10288
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
index 343f537..d25bf95 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLImageManager.cs
@@ -59,6 +59,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
59 private C5.IntervalHeap<J2KImage> m_priorityQueue = new C5.IntervalHeap<J2KImage>(10, new J2KImageComparer()); 59 private C5.IntervalHeap<J2KImage> m_priorityQueue = new C5.IntervalHeap<J2KImage>(10, new J2KImageComparer());
60 private object m_syncRoot = new object(); 60 private object m_syncRoot = new object();
61 61
62 private IHyperAssetService m_hyperAssets;
63
62 public LLClientView Client { get { return m_client; } } 64 public LLClientView Client { get { return m_client; } }
63 public AssetBase MissingImage { get { return m_missingImage; } } 65 public AssetBase MissingImage { get { return m_missingImage; } }
64 66
@@ -74,6 +76,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
74 m_log.Error("[ClientView] - Couldn't set missing image asset, falling back to missing image packet. This is known to crash the client"); 76 m_log.Error("[ClientView] - Couldn't set missing image asset, falling back to missing image packet. This is known to crash the client");
75 77
76 m_j2kDecodeModule = pJ2kDecodeModule; 78 m_j2kDecodeModule = pJ2kDecodeModule;
79 m_hyperAssets = client.Scene.RequestModuleInterface<IHyperAssetService>();
77 } 80 }
78 81
79 /// <summary> 82 /// <summary>
@@ -146,6 +149,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
146 imgrequest = new J2KImage(this); 149 imgrequest = new J2KImage(this);
147 imgrequest.J2KDecoder = m_j2kDecodeModule; 150 imgrequest.J2KDecoder = m_j2kDecodeModule;
148 imgrequest.AssetService = m_assetCache; 151 imgrequest.AssetService = m_assetCache;
152 imgrequest.AgentID = m_client.AgentId;
153 imgrequest.HyperAssets = m_hyperAssets;
149 imgrequest.DiscardLevel = newRequest.DiscardLevel; 154 imgrequest.DiscardLevel = newRequest.DiscardLevel;
150 imgrequest.StartPacket = Math.Max(1, newRequest.PacketNumber); 155 imgrequest.StartPacket = Math.Max(1, newRequest.PacketNumber);
151 imgrequest.Priority = newRequest.Priority; 156 imgrequest.Priority = newRequest.Priority;
@@ -162,47 +167,48 @@ namespace OpenSim.Region.ClientStack.LindenUDP
162 } 167 }
163 } 168 }
164 169
165 public bool ProcessImageQueue(int count, int maxpack) 170 public bool ProcessImageQueue(int packetsToSend)
166 { 171 {
167 J2KImage imagereq; 172 m_lastloopprocessed = DateTime.Now.Ticks;
168 int numCollected = 0; 173 int packetsSent = 0;
169 174
170 //lock (m_syncRoot) 175 while (packetsSent < packetsToSend)
171 //{ 176 {
172 m_lastloopprocessed = DateTime.Now.Ticks; 177 J2KImage image = GetHighestPriorityImage();
173 178
174 // This can happen during Close() 179 // If null was returned, the texture priority queue is currently empty
175 if (m_client == null) 180 if (image == null)
176 return false; 181 return false;
177
178 while ((imagereq = GetHighestPriorityImage()) != null)
179 {
180 if (imagereq.IsDecoded == true)
181 {
182 ++numCollected;
183 182
184 if (imagereq.SendPackets(m_client, maxpack)) 183 if (image.IsDecoded)
185 { 184 {
186 // Send complete. Destroy any knowledge of this transfer 185 int sent;
187 RemoveImageFromQueue(imagereq); 186 bool imageDone = image.SendPackets(m_client, packetsToSend - packetsSent, out sent);
188 } 187 packetsSent += sent;
189 }
190 188
191 if (numCollected == count) 189 // If the send is complete, destroy any knowledge of this transfer
192 break; 190 if (imageDone)
191 RemoveImageFromQueue(image);
192 }
193 else
194 {
195 // TODO: This is a limitation of how LLImageManager is currently
196 // written. Undecoded textures should not be going into the priority
197 // queue, because a high priority undecoded texture will clog up the
198 // pipeline for a client
199 return true;
193 } 200 }
194 //} 201 }
195 202
196 return m_priorityQueue.Count > 0; 203 return m_priorityQueue.Count > 0;
197 } 204 }
198 205
199 //Faux destructor 206 /// <summary>
207 /// Faux destructor
208 /// </summary>
200 public void Close() 209 public void Close()
201 { 210 {
202 m_shuttingdown = true; 211 m_shuttingdown = true;
203 m_j2kDecodeModule = null;
204 m_assetCache = null;
205 m_client = null;
206 } 212 }
207 213
208 #region Priority Queue Helpers 214 #region Priority Queue Helpers
@@ -213,13 +219,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
213 219
214 lock (m_syncRoot) 220 lock (m_syncRoot)
215 { 221 {
216
217 if (m_priorityQueue.Count > 0) 222 if (m_priorityQueue.Count > 0)
218 { 223 {
219 try 224 try { image = m_priorityQueue.FindMax(); }
220 {
221 image = m_priorityQueue.FindMax();
222 }
223 catch (Exception) { } 225 catch (Exception) { }
224 } 226 }
225 } 227 }
@@ -231,20 +233,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
231 image.PriorityQueueHandle = null; 233 image.PriorityQueueHandle = null;
232 234
233 lock (m_syncRoot) 235 lock (m_syncRoot)
234 try 236 try { m_priorityQueue.Add(ref image.PriorityQueueHandle, image); }
235 {
236 m_priorityQueue.Add(ref image.PriorityQueueHandle, image);
237 }
238 catch (Exception) { } 237 catch (Exception) { }
239 } 238 }
240 239
241 void RemoveImageFromQueue(J2KImage image) 240 void RemoveImageFromQueue(J2KImage image)
242 { 241 {
243 lock (m_syncRoot) 242 lock (m_syncRoot)
244 try 243 try { m_priorityQueue.Delete(image.PriorityQueueHandle); }
245 {
246 m_priorityQueue.Delete(image.PriorityQueueHandle);
247 }
248 catch (Exception) { } 244 catch (Exception) { }
249 } 245 }
250 246
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
index 871e8e8..4eee6b6 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClient.cs
@@ -28,8 +28,10 @@
28using System; 28using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Net; 30using System.Net;
31using log4net;
31using OpenSim.Framework; 32using OpenSim.Framework;
32using OpenMetaverse; 33using OpenMetaverse;
34using OpenMetaverse.Packets;
33 35
34namespace OpenSim.Region.ClientStack.LindenUDP 36namespace OpenSim.Region.ClientStack.LindenUDP
35{ 37{
@@ -59,9 +61,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
59 /// </summary> 61 /// </summary>
60 public sealed class LLUDPClient 62 public sealed class LLUDPClient
61 { 63 {
64 // TODO: Make this a config setting
65 /// <summary>Percentage of the task throttle category that is allocated to avatar and prim
66 /// state updates</summary>
67 const float STATE_TASK_PERCENTAGE = 0.8f;
68
69 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
70
62 /// <summary>The number of packet categories to throttle on. If a throttle category is added 71 /// <summary>The number of packet categories to throttle on. If a throttle category is added
63 /// or removed, this number must also change</summary> 72 /// or removed, this number must also change</summary>
64 const int THROTTLE_CATEGORY_COUNT = 7; 73 const int THROTTLE_CATEGORY_COUNT = 8;
65 74
66 /// <summary>Fired when updated networking stats are produced for this client</summary> 75 /// <summary>Fired when updated networking stats are produced for this client</summary>
67 public event PacketStats OnPacketStats; 76 public event PacketStats OnPacketStats;
@@ -80,10 +89,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
80 /// <summary>Packets we have sent that need to be ACKed by the client</summary> 89 /// <summary>Packets we have sent that need to be ACKed by the client</summary>
81 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); 90 public readonly UnackedPacketCollection NeedAcks = new UnackedPacketCollection();
82 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary> 91 /// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
83 public readonly LocklessQueue<uint> PendingAcks = new LocklessQueue<uint>(); 92 public readonly OpenSim.Framework.LocklessQueue<uint> PendingAcks = new OpenSim.Framework.LocklessQueue<uint>();
84 93
85 /// <summary>Reference to the IClientAPI for this client</summary>
86 public LLClientView ClientAPI;
87 /// <summary>Current packet sequence number</summary> 94 /// <summary>Current packet sequence number</summary>
88 public int CurrentSequence; 95 public int CurrentSequence;
89 /// <summary>Current ping sequence number</summary> 96 /// <summary>Current ping sequence number</summary>
@@ -123,21 +130,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
123 private int m_packetsSentReported; 130 private int m_packetsSentReported;
124 131
125 /// <summary>Throttle bucket for this agent's connection</summary> 132 /// <summary>Throttle bucket for this agent's connection</summary>
126 private readonly TokenBucket throttle; 133 private readonly TokenBucket m_throttle;
127 /// <summary>Throttle buckets for each packet category</summary> 134 /// <summary>Throttle buckets for each packet category</summary>
128 private readonly TokenBucket[] throttleCategories; 135 private readonly TokenBucket[] m_throttleCategories;
129 /// <summary>Throttle rate defaults and limits</summary> 136 /// <summary>Throttle rate defaults and limits</summary>
130 private readonly ThrottleRates defaultThrottleRates; 137 private readonly ThrottleRates m_defaultThrottleRates;
131 /// <summary>Outgoing queues for throttled packets</summary> 138 /// <summary>Outgoing queues for throttled packets</summary>
132 private readonly LocklessQueue<OutgoingPacket>[] packetOutboxes = new LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT]; 139 private readonly OpenSim.Framework.LocklessQueue<OutgoingPacket>[] m_packetOutboxes = new OpenSim.Framework.LocklessQueue<OutgoingPacket>[THROTTLE_CATEGORY_COUNT];
133 /// <summary>A container that can hold one packet for each outbox, used to store 140 /// <summary>A container that can hold one packet for each outbox, used to store
134 /// dequeued packets that are being held for throttling</summary> 141 /// dequeued packets that are being held for throttling</summary>
135 private readonly OutgoingPacket[] nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT]; 142 private readonly OutgoingPacket[] m_nextPackets = new OutgoingPacket[THROTTLE_CATEGORY_COUNT];
136 /// <summary>An optimization to store the length of dequeued packets being held 143 /// <summary>Flags to prevent queue empty callbacks from stacking up on
137 /// for throttling. This avoids expensive calls to Packet.Length</summary> 144 /// top of each other</summary>
138 private readonly int[] nextPacketLengths = new int[THROTTLE_CATEGORY_COUNT]; 145 private readonly bool[] m_onQueueEmptyRunning = new bool[THROTTLE_CATEGORY_COUNT];
139 /// <summary>A reference to the LLUDPServer that is managing this client</summary> 146 /// <summary>A reference to the LLUDPServer that is managing this client</summary>
140 private readonly LLUDPServer udpServer; 147 private readonly LLUDPServer m_udpServer;
141 148
142 /// <summary> 149 /// <summary>
143 /// Default constructor 150 /// Default constructor
@@ -151,24 +158,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP
151 /// <param name="remoteEndPoint">Remote endpoint for this connection</param> 158 /// <param name="remoteEndPoint">Remote endpoint for this connection</param>
152 public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint) 159 public LLUDPClient(LLUDPServer server, ThrottleRates rates, TokenBucket parentThrottle, uint circuitCode, UUID agentID, IPEndPoint remoteEndPoint)
153 { 160 {
154 udpServer = server;
155 AgentID = agentID; 161 AgentID = agentID;
156 RemoteEndPoint = remoteEndPoint; 162 RemoteEndPoint = remoteEndPoint;
157 CircuitCode = circuitCode; 163 CircuitCode = circuitCode;
158 defaultThrottleRates = rates; 164 m_udpServer = server;
165 m_defaultThrottleRates = rates;
166 m_throttle = new TokenBucket(parentThrottle, rates.TotalLimit, rates.Total);
167 m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT];
159 168
160 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 169 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
161 packetOutboxes[i] = new LocklessQueue<OutgoingPacket>(); 170 {
162 171 ThrottleOutPacketType type = (ThrottleOutPacketType)i;
163 throttle = new TokenBucket(parentThrottle, 0, 0); 172
164 throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; 173 m_packetOutboxes[i] = new OpenSim.Framework.LocklessQueue<OutgoingPacket>();
165 throttleCategories[(int)ThrottleOutPacketType.Resend] = new TokenBucket(throttle, rates.ResendLimit, rates.Resend); 174 m_throttleCategories[i] = new TokenBucket(m_throttle, rates.GetLimit(type), rates.GetRate(type));
166 throttleCategories[(int)ThrottleOutPacketType.Land] = new TokenBucket(throttle, rates.LandLimit, rates.Land); 175 }
167 throttleCategories[(int)ThrottleOutPacketType.Wind] = new TokenBucket(throttle, rates.WindLimit, rates.Wind);
168 throttleCategories[(int)ThrottleOutPacketType.Cloud] = new TokenBucket(throttle, rates.CloudLimit, rates.Cloud);
169 throttleCategories[(int)ThrottleOutPacketType.Task] = new TokenBucket(throttle, rates.TaskLimit, rates.Task);
170 throttleCategories[(int)ThrottleOutPacketType.Texture] = new TokenBucket(throttle, rates.TextureLimit, rates.Texture);
171 throttleCategories[(int)ThrottleOutPacketType.Asset] = new TokenBucket(throttle, rates.AssetLimit, rates.Asset);
172 176
173 // Set the granularity variable used for retransmission calculations to 177 // Set the granularity variable used for retransmission calculations to
174 // the measured resolution of Environment.TickCount 178 // the measured resolution of Environment.TickCount
@@ -176,6 +180,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
176 180
177 // Default the retransmission timeout to three seconds 181 // Default the retransmission timeout to three seconds
178 RTO = 3000; 182 RTO = 3000;
183
184 // Initialize this to a sane value to prevent early disconnects
185 TickLastPacketReceived = Environment.TickCount;
179 } 186 }
180 187
181 /// <summary> 188 /// <summary>
@@ -183,8 +190,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
183 /// </summary> 190 /// </summary>
184 public void Shutdown() 191 public void Shutdown()
185 { 192 {
186 // TODO: Do we need to invalidate the circuit?
187 IsConnected = false; 193 IsConnected = false;
194 NeedAcks.Clear();
195 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
196 {
197 m_packetOutboxes[i].Clear();
198 m_nextPackets[i] = null;
199 }
200 OnPacketStats = null;
201 OnQueueEmpty = null;
188 } 202 }
189 203
190 /// <summary> 204 /// <summary>
@@ -200,13 +214,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
200 info.pendingAcks = new Dictionary<uint, uint>(); 214 info.pendingAcks = new Dictionary<uint, uint>();
201 info.needAck = new Dictionary<uint, byte[]>(); 215 info.needAck = new Dictionary<uint, byte[]>();
202 216
203 info.resendThrottle = throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate; 217 info.resendThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate;
204 info.landThrottle = throttleCategories[(int)ThrottleOutPacketType.Land].DripRate; 218 info.landThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate;
205 info.windThrottle = throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate; 219 info.windThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate;
206 info.cloudThrottle = throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate; 220 info.cloudThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate;
207 info.taskThrottle = throttleCategories[(int)ThrottleOutPacketType.Task].DripRate; 221 info.taskThrottle = m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate + m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate;
208 info.assetThrottle = throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate; 222 info.assetThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate;
209 info.textureThrottle = throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate; 223 info.textureThrottle = m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate;
210 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle + 224 info.totalThrottle = info.resendThrottle + info.landThrottle + info.windThrottle + info.cloudThrottle +
211 info.taskThrottle + info.assetThrottle + info.textureThrottle; 225 info.taskThrottle + info.assetThrottle + info.textureThrottle;
212 226
@@ -267,6 +281,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
267 adjData = throttleData; 281 adjData = throttleData;
268 } 282 }
269 283
284 // 0.125f converts from bits to bytes
270 int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 285 int resend = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
271 int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 286 int land = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
272 int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 287 int wind = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
@@ -274,22 +289,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP
274 int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 289 int task = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
275 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4; 290 int texture = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); pos += 4;
276 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f); 291 int asset = (int)(BitConverter.ToSingle(adjData, pos) * 0.125f);
292 // State is a subcategory of task that we allocate a percentage to
293 int state = (int)((float)task * STATE_TASK_PERCENTAGE);
294 task -= state;
295
296 int ceiling = Int32.MaxValue;
297 if (m_defaultThrottleRates.Total != 0)
298 {
299 ceiling = m_defaultThrottleRates.Total;
300 if (ceiling < Packet.MTU) ceiling = Packet.MTU;
301 }
277 302
278 resend = (resend <= defaultThrottleRates.ResendLimit) ? resend : defaultThrottleRates.ResendLimit; 303 resend = Utils.Clamp(resend, Packet.MTU, ceiling);
279 land = (land <= defaultThrottleRates.LandLimit) ? land : defaultThrottleRates.LandLimit; 304 land = Utils.Clamp(land, Packet.MTU, ceiling);
280 wind = (wind <= defaultThrottleRates.WindLimit) ? wind : defaultThrottleRates.WindLimit; 305 wind = Utils.Clamp(wind, Packet.MTU, ceiling);
281 cloud = (cloud <= defaultThrottleRates.CloudLimit) ? cloud : defaultThrottleRates.CloudLimit; 306 cloud = Utils.Clamp(cloud, Packet.MTU, ceiling);
282 task = (task <= defaultThrottleRates.TaskLimit) ? task : defaultThrottleRates.TaskLimit; 307 task = Utils.Clamp(task, Packet.MTU, ceiling);
283 texture = (texture <= defaultThrottleRates.TextureLimit) ? texture : defaultThrottleRates.TextureLimit; 308 texture = Utils.Clamp(texture, Packet.MTU, ceiling);
284 asset = (asset <= defaultThrottleRates.AssetLimit) ? asset : defaultThrottleRates.AssetLimit; 309 asset = Utils.Clamp(asset, Packet.MTU, ceiling);
285 310 state = Utils.Clamp(state, Packet.MTU, ceiling);
286 SetThrottle(ThrottleOutPacketType.Resend, resend); 311
287 SetThrottle(ThrottleOutPacketType.Land, land); 312 int total = resend + land + wind + cloud + task + texture + asset + state;
288 SetThrottle(ThrottleOutPacketType.Wind, wind); 313 int taskTotal = task + state;
289 SetThrottle(ThrottleOutPacketType.Cloud, cloud); 314
290 SetThrottle(ThrottleOutPacketType.Task, task); 315 m_log.DebugFormat("[LLUDPCLIENT]: {0} is setting throttles. Resend={1}, Land={2}, Wind={3}, Cloud={4}, Task={5}, Texture={6}, Asset={7}, State={8}, Total={9}",
291 SetThrottle(ThrottleOutPacketType.Texture, texture); 316 AgentID, resend, land, wind, cloud, task, texture, asset, state, total);
292 SetThrottle(ThrottleOutPacketType.Asset, asset); 317
318 SetThrottle(ThrottleOutPacketType.Resend, resend, resend);
319 SetThrottle(ThrottleOutPacketType.Land, land, land);
320 SetThrottle(ThrottleOutPacketType.Wind, wind, wind);
321 SetThrottle(ThrottleOutPacketType.Cloud, cloud, cloud);
322 SetThrottle(ThrottleOutPacketType.Task, task, taskTotal);
323 SetThrottle(ThrottleOutPacketType.Texture, texture, texture);
324 SetThrottle(ThrottleOutPacketType.Asset, asset, asset);
325 SetThrottle(ThrottleOutPacketType.State, state, taskTotal);
293 } 326 }
294 327
295 public byte[] GetThrottlesPacked() 328 public byte[] GetThrottlesPacked()
@@ -297,25 +330,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
297 byte[] data = new byte[7 * 4]; 330 byte[] data = new byte[7 * 4];
298 int i = 0; 331 int i = 0;
299 332
300 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4; 333 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Resend].DripRate), 0, data, i, 4); i += 4;
301 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4; 334 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Land].DripRate), 0, data, i, 4); i += 4;
302 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4; 335 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Wind].DripRate), 0, data, i, 4); i += 4;
303 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4; 336 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Cloud].DripRate), 0, data, i, 4); i += 4;
304 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Task].DripRate), 0, data, i, 4); i += 4; 337 Buffer.BlockCopy(Utils.FloatToBytes((float)(m_throttleCategories[(int)ThrottleOutPacketType.Task].DripRate) +
305 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4; 338 m_throttleCategories[(int)ThrottleOutPacketType.State].DripRate), 0, data, i, 4); i += 4;
306 Buffer.BlockCopy(Utils.FloatToBytes((float)throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4; 339 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Texture].DripRate), 0, data, i, 4); i += 4;
340 Buffer.BlockCopy(Utils.FloatToBytes((float)m_throttleCategories[(int)ThrottleOutPacketType.Asset].DripRate), 0, data, i, 4); i += 4;
307 341
308 return data; 342 return data;
309 } 343 }
310 344
311 public void SetThrottle(ThrottleOutPacketType category, int rate) 345 public void SetThrottle(ThrottleOutPacketType category, int rate, int maxBurst)
312 { 346 {
313 int i = (int)category; 347 int i = (int)category;
314 if (i >= 0 && i < throttleCategories.Length) 348 if (i >= 0 && i < m_throttleCategories.Length)
315 { 349 {
316 TokenBucket bucket = throttleCategories[(int)category]; 350 TokenBucket bucket = m_throttleCategories[(int)category];
317 bucket.MaxBurst = rate;
318 bucket.DripRate = rate; 351 bucket.DripRate = rate;
352 bucket.MaxBurst = maxBurst;
319 } 353 }
320 } 354 }
321 355
@@ -323,12 +357,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
323 { 357 {
324 int category = (int)packet.Category; 358 int category = (int)packet.Category;
325 359
326 if (category >= 0 && category < packetOutboxes.Length) 360 if (category >= 0 && category < m_packetOutboxes.Length)
327 { 361 {
328 LocklessQueue<OutgoingPacket> queue = packetOutboxes[category]; 362 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue = m_packetOutboxes[category];
329 TokenBucket bucket = throttleCategories[category]; 363 TokenBucket bucket = m_throttleCategories[category];
330 364
331 if (throttleCategories[category].RemoveTokens(packet.Buffer.DataLength)) 365 if (m_throttleCategories[category].RemoveTokens(packet.Buffer.DataLength))
332 { 366 {
333 // Enough tokens were removed from the bucket, the packet will not be queued 367 // Enough tokens were removed from the bucket, the packet will not be queued
334 return false; 368 return false;
@@ -357,24 +391,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP
357 public bool DequeueOutgoing() 391 public bool DequeueOutgoing()
358 { 392 {
359 OutgoingPacket packet; 393 OutgoingPacket packet;
360 LocklessQueue<OutgoingPacket> queue; 394 OpenSim.Framework.LocklessQueue<OutgoingPacket> queue;
361 TokenBucket bucket; 395 TokenBucket bucket;
362 bool packetSent = false; 396 bool packetSent = false;
363 397
364 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) 398 for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++)
365 { 399 {
366 bucket = throttleCategories[i]; 400 bucket = m_throttleCategories[i];
367 401
368 if (nextPackets[i] != null) 402 if (m_nextPackets[i] != null)
369 { 403 {
370 // This bucket was empty the last time we tried to send a packet, 404 // This bucket was empty the last time we tried to send a packet,
371 // leaving a dequeued packet still waiting to be sent out. Try to 405 // leaving a dequeued packet still waiting to be sent out. Try to
372 // send it again 406 // send it again
373 if (bucket.RemoveTokens(nextPacketLengths[i])) 407 OutgoingPacket nextPacket = m_nextPackets[i];
408 if (bucket.RemoveTokens(nextPacket.Buffer.DataLength))
374 { 409 {
375 // Send the packet 410 // Send the packet
376 udpServer.SendPacketFinal(nextPackets[i]); 411 m_udpServer.SendPacketFinal(nextPacket);
377 nextPackets[i] = null; 412 m_nextPackets[i] = null;
378 packetSent = true; 413 packetSent = true;
379 } 414 }
380 } 415 }
@@ -382,7 +417,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
382 { 417 {
383 // No dequeued packet waiting to be sent, try to pull one off 418 // No dequeued packet waiting to be sent, try to pull one off
384 // this queue 419 // this queue
385 queue = packetOutboxes[i]; 420 queue = m_packetOutboxes[i];
386 if (queue.Dequeue(out packet)) 421 if (queue.Dequeue(out packet))
387 { 422 {
388 // A packet was pulled off the queue. See if we have 423 // A packet was pulled off the queue. See if we have
@@ -390,23 +425,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
390 if (bucket.RemoveTokens(packet.Buffer.DataLength)) 425 if (bucket.RemoveTokens(packet.Buffer.DataLength))
391 { 426 {
392 // Send the packet 427 // Send the packet
393 udpServer.SendPacketFinal(packet); 428 m_udpServer.SendPacketFinal(packet);
394 packetSent = true; 429 packetSent = true;
395 } 430 }
396 else 431 else
397 { 432 {
398 // Save the dequeued packet and the length calculation for 433 // Save the dequeued packet for the next iteration
399 // the next iteration 434 m_nextPackets[i] = packet;
400 nextPackets[i] = packet;
401 nextPacketLengths[i] = packet.Buffer.DataLength;
402 } 435 }
436
437 // If the queue is empty after this dequeue, fire the queue
438 // empty callback now so it has a chance to fill before we
439 // get back here
440 if (queue.Count == 0)
441 BeginFireQueueEmpty(i);
403 } 442 }
404 else 443 else
405 { 444 {
406 // No packets in this queue. Fire the queue empty callback 445 // No packets in this queue. Fire the queue empty callback
407 QueueEmpty callback = OnQueueEmpty; 446 // if it has not been called recently
408 if (callback != null) 447 BeginFireQueueEmpty(i);
409 callback((ThrottleOutPacketType)i);
410 } 448 }
411 } 449 }
412 } 450 }
@@ -414,6 +452,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
414 return packetSent; 452 return packetSent;
415 } 453 }
416 454
455 /// <summary>
456 /// Called when an ACK packet is received and a round-trip time for a
457 /// packet is calculated. This is used to calculate the smoothed
458 /// round-trip time, round trip time variance, and finally the
459 /// retransmission timeout
460 /// </summary>
461 /// <param name="r">Round-trip time of a single packet and its
462 /// acknowledgement</param>
417 public void UpdateRoundTrip(float r) 463 public void UpdateRoundTrip(float r)
418 { 464 {
419 const float ALPHA = 0.125f; 465 const float ALPHA = 0.125f;
@@ -435,8 +481,44 @@ namespace OpenSim.Region.ClientStack.LindenUDP
435 481
436 // Always round retransmission timeout up to two seconds 482 // Always round retransmission timeout up to two seconds
437 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR))); 483 RTO = Math.Max(2000, (int)(SRTT + Math.Max(G, K * RTTVAR)));
438 //Logger.Debug("Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " + 484 //m_log.Debug("[LLUDPCLIENT]: Setting agent " + this.Agent.FullName + "'s RTO to " + RTO + "ms with an RTTVAR of " +
439 // RTTVAR + " based on new RTT of " + r + "ms"); 485 // RTTVAR + " based on new RTT of " + r + "ms");
440 } 486 }
487
488 /// <summary>
489 /// Does an early check to see if this queue empty callback is already
490 /// running, then asynchronously firing the event
491 /// </summary>
492 /// <param name="throttleIndex">Throttle category to fire the callback
493 /// for</param>
494 private void BeginFireQueueEmpty(int throttleIndex)
495 {
496 if (!m_onQueueEmptyRunning[throttleIndex])
497 Util.FireAndForget(FireQueueEmpty, throttleIndex);
498 }
499
500 /// <summary>
501 /// Checks to see if this queue empty callback is already running,
502 /// then firing the event
503 /// </summary>
504 /// <param name="o">Throttle category to fire the callback for, stored
505 /// as an object to match the WaitCallback delegate signature</param>
506 private void FireQueueEmpty(object o)
507 {
508 int i = (int)o;
509 ThrottleOutPacketType type = (ThrottleOutPacketType)i;
510 QueueEmpty callback = OnQueueEmpty;
511
512 if (callback != null)
513 {
514 if (!m_onQueueEmptyRunning[i])
515 {
516 m_onQueueEmptyRunning[i] = true;
517 try { callback(type); }
518 catch (Exception e) { m_log.Error("[LLUDPCLIENT]: OnQueueEmpty(" + type + ") threw an exception: " + e.Message, e); }
519 m_onQueueEmptyRunning[i] = false;
520 }
521 }
522 }
441 } 523 }
442} 524}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs
deleted file mode 100644
index dbb9861..0000000
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPClientCollection.cs
+++ /dev/null
@@ -1,282 +0,0 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Net;
31using OpenSim.Framework;
32using OpenMetaverse;
33
34using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim;
35
36namespace OpenSim.Region.ClientStack.LindenUDP
37{
38 public sealed class UDPClientCollection
39 {
40 Dictionary<UUID, LLUDPClient> Dictionary1;
41 Dictionary<IPEndPoint, LLUDPClient> Dictionary2;
42 LLUDPClient[] Array;
43 ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl();
44 object m_sync = new object();
45
46 public UDPClientCollection()
47 {
48 Dictionary1 = new Dictionary<UUID, LLUDPClient>();
49 Dictionary2 = new Dictionary<IPEndPoint, LLUDPClient>();
50 Array = new LLUDPClient[0];
51 }
52
53 public UDPClientCollection(int capacity)
54 {
55 Dictionary1 = new Dictionary<UUID, LLUDPClient>(capacity);
56 Dictionary2 = new Dictionary<IPEndPoint, LLUDPClient>(capacity);
57 Array = new LLUDPClient[0];
58 }
59
60 public void Add(UUID key1, IPEndPoint key2, LLUDPClient value)
61 {
62 //rwLock.EnterWriteLock();
63
64 //try
65 //{
66 // if (Dictionary1.ContainsKey(key1))
67 // {
68 // if (!Dictionary2.ContainsKey(key2))
69 // throw new ArgumentException("key1 exists in the dictionary but not key2");
70 // }
71 // else if (Dictionary2.ContainsKey(key2))
72 // {
73 // if (!Dictionary1.ContainsKey(key1))
74 // throw new ArgumentException("key2 exists in the dictionary but not key1");
75 // }
76
77 // Dictionary1[key1] = value;
78 // Dictionary2[key2] = value;
79
80 // LLUDPClient[] oldArray = Array;
81 // int oldLength = oldArray.Length;
82
83 // LLUDPClient[] newArray = new LLUDPClient[oldLength + 1];
84 // for (int i = 0; i < oldLength; i++)
85 // newArray[i] = oldArray[i];
86 // newArray[oldLength] = value;
87
88 // Array = newArray;
89 //}
90 //finally { rwLock.ExitWriteLock(); }
91
92 lock (m_sync)
93 {
94 if (Dictionary1.ContainsKey(key1))
95 {
96 if (!Dictionary2.ContainsKey(key2))
97 throw new ArgumentException("key1 exists in the dictionary but not key2");
98 }
99 else if (Dictionary2.ContainsKey(key2))
100 {
101 if (!Dictionary1.ContainsKey(key1))
102 throw new ArgumentException("key2 exists in the dictionary but not key1");
103 }
104
105 Dictionary1[key1] = value;
106 Dictionary2[key2] = value;
107
108 LLUDPClient[] oldArray = Array;
109 int oldLength = oldArray.Length;
110
111 LLUDPClient[] newArray = new LLUDPClient[oldLength + 1];
112 for (int i = 0; i < oldLength; i++)
113 newArray[i] = oldArray[i];
114 newArray[oldLength] = value;
115
116 Array = newArray;
117 }
118
119 }
120
121 public bool Remove(UUID key1, IPEndPoint key2)
122 {
123 //rwLock.EnterWriteLock();
124
125 //try
126 //{
127 // LLUDPClient value;
128 // if (Dictionary1.TryGetValue(key1, out value))
129 // {
130 // Dictionary1.Remove(key1);
131 // Dictionary2.Remove(key2);
132
133 // LLUDPClient[] oldArray = Array;
134 // int oldLength = oldArray.Length;
135
136 // LLUDPClient[] newArray = new LLUDPClient[oldLength - 1];
137 // int j = 0;
138 // for (int i = 0; i < oldLength; i++)
139 // {
140 // if (oldArray[i] != value)
141 // newArray[j++] = oldArray[i];
142 // }
143
144 // Array = newArray;
145 // return true;
146 // }
147 //}
148 //finally { rwLock.ExitWriteLock(); }
149
150 //return false;
151
152 lock (m_sync)
153 {
154 LLUDPClient value;
155 if (Dictionary1.TryGetValue(key1, out value))
156 {
157 Dictionary1.Remove(key1);
158 Dictionary2.Remove(key2);
159
160 LLUDPClient[] oldArray = Array;
161 int oldLength = oldArray.Length;
162
163 LLUDPClient[] newArray = new LLUDPClient[oldLength - 1];
164 int j = 0;
165 for (int i = 0; i < oldLength; i++)
166 {
167 if (oldArray[i] != value)
168 newArray[j++] = oldArray[i];
169 }
170
171 Array = newArray;
172 return true;
173 }
174 }
175
176 return false;
177
178 }
179
180 public void Clear()
181 {
182 //rwLock.EnterWriteLock();
183
184 //try
185 //{
186 // Dictionary1.Clear();
187 // Dictionary2.Clear();
188 // Array = new LLUDPClient[0];
189 //}
190 //finally { rwLock.ExitWriteLock(); }
191
192 lock (m_sync)
193 {
194 Dictionary1.Clear();
195 Dictionary2.Clear();
196 Array = new LLUDPClient[0];
197 }
198
199 }
200
201 public int Count
202 {
203 get { return Array.Length; }
204 }
205
206 public bool ContainsKey(UUID key)
207 {
208 return Dictionary1.ContainsKey(key);
209 }
210
211 public bool ContainsKey(IPEndPoint key)
212 {
213 return Dictionary2.ContainsKey(key);
214 }
215
216 public bool TryGetValue(UUID key, out LLUDPClient value)
217 {
218 ////bool success;
219 ////bool doLock = !rwLock.IsUpgradeableReadLockHeld;
220 ////if (doLock) rwLock.EnterReadLock();
221
222 ////try { success = Dictionary1.TryGetValue(key, out value); }
223 ////finally { if (doLock) rwLock.ExitReadLock(); }
224
225 ////return success;
226
227 lock (m_sync)
228 return Dictionary1.TryGetValue(key, out value);
229
230 //try
231 //{
232 // return Dictionary1.TryGetValue(key, out value);
233 //}
234 //catch { }
235 //value = null;
236 //return false;
237 }
238
239 public bool TryGetValue(IPEndPoint key, out LLUDPClient value)
240 {
241 ////bool success;
242 ////bool doLock = !rwLock.IsUpgradeableReadLockHeld;
243 ////if (doLock) rwLock.EnterReadLock();
244
245 ////try { success = Dictionary2.TryGetValue(key, out value); }
246 ////finally { if (doLock) rwLock.ExitReadLock(); }
247
248 ////return success;
249
250 lock (m_sync)
251 return Dictionary2.TryGetValue(key, out value);
252
253 //try
254 //{
255 // return Dictionary2.TryGetValue(key, out value);
256 //}
257 //catch { }
258 //value = null;
259 //return false;
260
261 }
262
263 public void ForEach(Action<LLUDPClient> action)
264 {
265 //bool doLock = !rwLock.IsUpgradeableReadLockHeld;
266 //if (doLock) rwLock.EnterUpgradeableReadLock();
267
268 //try { Parallel.ForEach<LLUDPClient>(Array, action); }
269 //finally { if (doLock) rwLock.ExitUpgradeableReadLock(); }
270
271 LLUDPClient[] localArray = null;
272 lock (m_sync)
273 {
274 localArray = new LLUDPClient[Array.Length];
275 Array.CopyTo(localArray, 0);
276 }
277
278 Parallel.ForEach<LLUDPClient>(localArray, action);
279
280 }
281 }
282}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
index c356f02..545a0bc 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLUDPServer.cs
@@ -96,7 +96,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
96 /// <summary>Incoming packets that are awaiting handling</summary> 96 /// <summary>Incoming packets that are awaiting handling</summary>
97 private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>(); 97 private OpenMetaverse.BlockingQueue<IncomingPacket> packetInbox = new OpenMetaverse.BlockingQueue<IncomingPacket>();
98 /// <summary></summary> 98 /// <summary></summary>
99 private UDPClientCollection clients = new UDPClientCollection(); 99 //private UDPClientCollection m_clients = new UDPClientCollection();
100 /// <summary>Bandwidth throttle for this UDP server</summary> 100 /// <summary>Bandwidth throttle for this UDP server</summary>
101 private TokenBucket m_throttle; 101 private TokenBucket m_throttle;
102 /// <summary>Bandwidth throttle rates for this UDP server</summary> 102 /// <summary>Bandwidth throttle rates for this UDP server</summary>
@@ -109,13 +109,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP
109 private Location m_location; 109 private Location m_location;
110 /// <summary>The measured resolution of Environment.TickCount</summary> 110 /// <summary>The measured resolution of Environment.TickCount</summary>
111 private float m_tickCountResolution; 111 private float m_tickCountResolution;
112 /// <summary>The size of the receive buffer for the UDP socket. This value
113 /// is passed up to the operating system and used in the system networking
114 /// stack. Use zero to leave this value as the default</summary>
115 private int m_recvBufferSize;
116 /// <summary>Flag to process packets asynchronously or synchronously</summary>
117 private bool m_asyncPacketHandling;
112 118
113 /// <summary>The measured resolution of Environment.TickCount</summary> 119 /// <summary>The measured resolution of Environment.TickCount</summary>
114 public float TickCountResolution { get { return m_tickCountResolution; } } 120 public float TickCountResolution { get { return m_tickCountResolution; } }
115 public Socket Server { get { return null; } } 121 public Socket Server { get { return null; } }
116 122
117 public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager) 123 public LLUDPServer(IPAddress listenIP, ref uint port, int proxyPortOffsetParm, bool allow_alternate_port, IConfigSource configSource, AgentCircuitManager circuitManager)
118 : base((int)port) 124 : base(listenIP, (int)port)
119 { 125 {
120 #region Environment.TickCount Measurement 126 #region Environment.TickCount Measurement
121 127
@@ -134,18 +140,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
134 #endregion Environment.TickCount Measurement 140 #endregion Environment.TickCount Measurement
135 141
136 m_circuitManager = circuitManager; 142 m_circuitManager = circuitManager;
143 int sceneThrottleBps = 0;
137 144
138 // TODO: Config support for throttling the entire connection 145 IConfig config = configSource.Configs["ClientStack.LindenUDP"];
139 m_throttle = new TokenBucket(null, 0, 0); 146 if (config != null)
147 {
148 m_asyncPacketHandling = config.GetBoolean("async_packet_handling", false);
149 m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0);
150 sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0);
151 }
152
153 m_throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps);
140 m_throttleRates = new ThrottleRates(configSource); 154 m_throttleRates = new ThrottleRates(configSource);
141 } 155 }
142 156
143 public new void Start() 157 public void Start()
144 { 158 {
145 if (m_scene == null) 159 if (m_scene == null)
146 throw new InvalidOperationException("Cannot LLUDPServer.Start() without an IScene reference"); 160 throw new InvalidOperationException("[LLUDPSERVER]: Cannot LLUDPServer.Start() without an IScene reference");
161
162 m_log.Info("[LLUDPSERVER]: Starting the LLUDP server in " + (m_asyncPacketHandling ? "asynchronous" : "synchronous") + " mode");
147 163
148 base.Start(); 164 base.Start(m_recvBufferSize, m_asyncPacketHandling);
149 165
150 // Start the incoming packet processing thread 166 // Start the incoming packet processing thread
151 Thread incomingThread = new Thread(IncomingPacketHandler); 167 Thread incomingThread = new Thread(IncomingPacketHandler);
@@ -181,24 +197,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
181 return x == m_location; 197 return x == m_location;
182 } 198 }
183 199
184 public void RemoveClient(IClientAPI client)
185 {
186 m_scene.ClientManager.Remove(client.CircuitCode);
187 client.Close(false);
188
189 LLUDPClient udpClient;
190 if (clients.TryGetValue(client.AgentId, out udpClient))
191 {
192 m_log.Debug("[LLUDPSERVER]: Removing LLUDPClient for " + client.Name + " in " + m_scene.RegionInfo.RegionName);
193 udpClient.Shutdown();
194 clients.Remove(client.AgentId, udpClient.RemoteEndPoint);
195 }
196 else
197 {
198 m_log.Warn("[LLUDPSERVER]: Failed to remove LLUDPClient for " + client.Name);
199 }
200 }
201
202 public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting) 200 public void BroadcastPacket(Packet packet, ThrottleOutPacketType category, bool sendToPausedAgents, bool allowSplitting)
203 { 201 {
204 // CoarseLocationUpdate packets cannot be split in an automated way 202 // CoarseLocationUpdate packets cannot be split in an automated way
@@ -216,30 +214,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP
216 for (int i = 0; i < packetCount; i++) 214 for (int i = 0; i < packetCount; i++)
217 { 215 {
218 byte[] data = datas[i]; 216 byte[] data = datas[i];
219 clients.ForEach( 217 m_scene.ClientManager.ForEach(
220 delegate(LLUDPClient client) 218 delegate(IClientAPI client)
221 { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); 219 {
220 if (client is LLClientView)
221 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category);
222 }
223 );
222 } 224 }
223 } 225 }
224 else 226 else
225 { 227 {
226 byte[] data = packet.ToBytes(); 228 byte[] data = packet.ToBytes();
227 clients.ForEach( 229 m_scene.ClientManager.ForEach(
228 delegate(LLUDPClient client) 230 delegate(IClientAPI client)
229 { SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); }); 231 {
232 if (client is LLClientView)
233 SendPacketData(((LLClientView)client).UDPClient, data, packet.Type, category);
234 }
235 );
230 } 236 }
231 } 237 }
232 238
233 public void SendPacket(UUID agentID, Packet packet, ThrottleOutPacketType category, bool allowSplitting) 239 public void SendPacket(LLUDPClient udpClient, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
234 {
235 LLUDPClient client;
236 if (clients.TryGetValue(agentID, out client))
237 SendPacket(client, packet, category, allowSplitting);
238 else
239 m_log.Warn("[LLUDPSERVER]: Attempted to send a packet to unknown agentID " + agentID);
240 }
241
242 public void SendPacket(LLUDPClient client, Packet packet, ThrottleOutPacketType category, bool allowSplitting)
243 { 240 {
244 // CoarseLocationUpdate packets cannot be split in an automated way 241 // CoarseLocationUpdate packets cannot be split in an automated way
245 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting) 242 if (packet.Type == PacketType.CoarseLocationUpdate && allowSplitting)
@@ -256,25 +253,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
256 for (int i = 0; i < packetCount; i++) 253 for (int i = 0; i < packetCount; i++)
257 { 254 {
258 byte[] data = datas[i]; 255 byte[] data = datas[i];
259 SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); 256 SendPacketData(udpClient, data, packet.Type, category);
260 } 257 }
261 } 258 }
262 else 259 else
263 { 260 {
264 byte[] data = packet.ToBytes(); 261 byte[] data = packet.ToBytes();
265 SendPacketData(client, data, data.Length, packet.Type, packet.Header.Zerocoded, category); 262 SendPacketData(udpClient, data, packet.Type, category);
266 } 263 }
267 } 264 }
268 265
269 public void SendPacketData(LLUDPClient client, byte[] data, int dataLength, PacketType type, bool doZerocode, ThrottleOutPacketType category) 266 public void SendPacketData(LLUDPClient udpClient, byte[] data, PacketType type, ThrottleOutPacketType category)
270 { 267 {
268 int dataLength = data.Length;
269 bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0;
270
271 // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. 271 // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum.
272 // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting 272 // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting
273 // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here 273 // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here
274 // to accomodate for both common scenarios and provide ample room for ACK appending in both 274 // to accomodate for both common scenarios and provide ample room for ACK appending in both
275 int bufferSize = (dataLength > 180) ? Packet.MTU : 200; 275 int bufferSize = (dataLength > 180) ? Packet.MTU : 200;
276 276
277 UDPPacketBuffer buffer = new UDPPacketBuffer(client.RemoteEndPoint, bufferSize); 277 UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize);
278 278
279 // Zerocode if needed 279 // Zerocode if needed
280 if (doZerocode) 280 if (doZerocode)
@@ -285,17 +285,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
285 // The packet grew larger than the bufferSize while zerocoding. 285 // The packet grew larger than the bufferSize while zerocoding.
286 // Remove the MSG_ZEROCODED flag and send the unencoded data 286 // Remove the MSG_ZEROCODED flag and send the unencoded data
287 // instead 287 // instead
288 m_log.Info("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding. Removing MSG_ZEROCODED flag"); 288 m_log.Debug("[LLUDPSERVER]: Packet exceeded buffer size during zerocoding for " + type + ". Removing MSG_ZEROCODED flag");
289 data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); 289 data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED);
290 //
291 buffer = new UDPPacketBuffer(client.RemoteEndPoint, dataLength);
292 //
293 Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); 290 Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
294 } 291 }
295 } 292 }
296 else 293 else
297 { 294 {
298 // ??? will it fit?
299 Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); 295 Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength);
300 } 296 }
301 buffer.DataLength = dataLength; 297 buffer.DataLength = dataLength;
@@ -303,7 +299,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
303 #region Queue or Send 299 #region Queue or Send
304 300
305 // Look up the UDPClient this is going to 301 // Look up the UDPClient this is going to
306 OutgoingPacket outgoingPacket = new OutgoingPacket(client, buffer, category); 302 OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category);
307 303
308 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) 304 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
309 SendPacketFinal(outgoingPacket); 305 SendPacketFinal(outgoingPacket);
@@ -311,18 +307,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
311 #endregion Queue or Send 307 #endregion Queue or Send
312 } 308 }
313 309
314 public void SendAcks(LLUDPClient client) 310 public void SendAcks(LLUDPClient udpClient)
315 { 311 {
316 uint ack; 312 uint ack;
317 313
318 if (client.PendingAcks.Dequeue(out ack)) 314 if (udpClient.PendingAcks.Dequeue(out ack))
319 { 315 {
320 List<PacketAckPacket.PacketsBlock> blocks = new List<PacketAckPacket.PacketsBlock>(); 316 List<PacketAckPacket.PacketsBlock> blocks = new List<PacketAckPacket.PacketsBlock>();
321 PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock(); 317 PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock();
322 block.ID = ack; 318 block.ID = ack;
323 blocks.Add(block); 319 blocks.Add(block);
324 320
325 while (client.PendingAcks.Dequeue(out ack)) 321 while (udpClient.PendingAcks.Dequeue(out ack))
326 { 322 {
327 block = new PacketAckPacket.PacketsBlock(); 323 block = new PacketAckPacket.PacketsBlock();
328 block.ID = ack; 324 block.ID = ack;
@@ -333,22 +329,39 @@ namespace OpenSim.Region.ClientStack.LindenUDP
333 packet.Header.Reliable = false; 329 packet.Header.Reliable = false;
334 packet.Packets = blocks.ToArray(); 330 packet.Packets = blocks.ToArray();
335 331
336 SendPacket(client, packet, ThrottleOutPacketType.Unknown, true); 332 SendPacket(udpClient, packet, ThrottleOutPacketType.Unknown, true);
337 } 333 }
338 } 334 }
339 335
340 public void SendPing(LLUDPClient client) 336 public void SendPing(LLUDPClient udpClient)
341 { 337 {
342 IClientAPI api = client.ClientAPI; 338 StartPingCheckPacket pc = (StartPingCheckPacket)PacketPool.Instance.GetPacket(PacketType.StartPingCheck);
343 if (api != null) 339 pc.Header.Reliable = false;
344 api.SendStartPingCheck(client.CurrentPingSequence++); 340
341 OutgoingPacket oldestPacket = udpClient.NeedAcks.GetOldest();
342
343 pc.PingID.PingID = (byte)udpClient.CurrentPingSequence++;
344 pc.PingID.OldestUnacked = (oldestPacket != null) ? oldestPacket.SequenceNumber : 0;
345
346 SendPacket(udpClient, pc, ThrottleOutPacketType.Unknown, false);
345 } 347 }
346 348
347 public void ResendUnacked(LLUDPClient client) 349 public void ResendUnacked(LLUDPClient udpClient)
348 { 350 {
349 if (client.NeedAcks.Count > 0) 351 if (udpClient.IsConnected && udpClient.NeedAcks.Count > 0)
350 { 352 {
351 List<OutgoingPacket> expiredPackets = client.NeedAcks.GetExpiredPackets(client.RTO); 353 // Disconnect an agent if no packets are received for some time
354 //FIXME: Make 60 an .ini setting
355 if (Environment.TickCount - udpClient.TickLastPacketReceived > 1000 * 60)
356 {
357 m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + udpClient.AgentID);
358
359 RemoveClient(udpClient);
360 return;
361 }
362
363 // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO
364 List<OutgoingPacket> expiredPackets = udpClient.NeedAcks.GetExpiredPackets(udpClient.RTO);
352 365
353 if (expiredPackets != null) 366 if (expiredPackets != null)
354 { 367 {
@@ -357,54 +370,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP
357 { 370 {
358 OutgoingPacket outgoingPacket = expiredPackets[i]; 371 OutgoingPacket outgoingPacket = expiredPackets[i];
359 372
360 // FIXME: Make this an .ini setting 373 //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed",
361 if (outgoingPacket.ResendCount < 3) 374 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount);
362 {
363 //Logger.Debug(String.Format("Resending packet #{0} (attempt {1}), {2}ms have passed",
364 // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount));
365
366 // Set the resent flag
367 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
368 outgoingPacket.Category = ThrottleOutPacketType.Resend;
369 375
370 // The TickCount will be set to the current time when the packet 376 // Set the resent flag
371 // is actually sent out again 377 outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT);
372 outgoingPacket.TickCount = 0; 378 outgoingPacket.Category = ThrottleOutPacketType.Resend;
373 379
374 // Bump up the resend count on this packet 380 // The TickCount will be set to the current time when the packet
375 Interlocked.Increment(ref outgoingPacket.ResendCount); 381 // is actually sent out again
376 //Interlocked.Increment(ref Stats.ResentPackets); 382 outgoingPacket.TickCount = 0;
377 383
378 // Queue or (re)send the packet 384 // Bump up the resend count on this packet
379 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket)) 385 Interlocked.Increment(ref outgoingPacket.ResendCount);
380 SendPacketFinal(outgoingPacket); 386 //Interlocked.Increment(ref Stats.ResentPackets);
381 }
382 else
383 {
384 m_log.DebugFormat("[LLUDPSERVER]: Dropping packet #{0} for agent {1} after {2} failed attempts",
385 outgoingPacket.SequenceNumber, outgoingPacket.Client.RemoteEndPoint, outgoingPacket.ResendCount);
386 387
387 lock (client.NeedAcks.SyncRoot) 388 // Requeue or resend the packet
388 client.NeedAcks.RemoveUnsafe(outgoingPacket.SequenceNumber); 389 if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket))
389 390 SendPacketFinal(outgoingPacket);
390 //Interlocked.Increment(ref Stats.DroppedPackets);
391
392 // Disconnect an agent if no packets are received for some time
393 //FIXME: Make 60 an .ini setting
394 if (Environment.TickCount - client.TickLastPacketReceived > 1000 * 60)
395 {
396 m_log.Warn("[LLUDPSERVER]: Ack timeout, disconnecting " + client.ClientAPI.Name);
397
398 RemoveClient(client.ClientAPI);
399 return;
400 }
401 }
402 } 391 }
403 } 392 }
404 } 393 }
405 } 394 }
406 395
407 public void Flush() 396 public void Flush(LLUDPClient udpClient)
408 { 397 {
409 // FIXME: Implement? 398 // FIXME: Implement?
410 } 399 }
@@ -415,7 +404,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
415 byte flags = buffer.Data[0]; 404 byte flags = buffer.Data[0];
416 bool isResend = (flags & Helpers.MSG_RESENT) != 0; 405 bool isResend = (flags & Helpers.MSG_RESENT) != 0;
417 bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; 406 bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0;
418 LLUDPClient client = outgoingPacket.Client; 407 LLUDPClient udpClient = outgoingPacket.Client;
408
409 if (!udpClient.IsConnected)
410 return;
419 411
420 // Keep track of when this packet was sent out (right now) 412 // Keep track of when this packet was sent out (right now)
421 outgoingPacket.TickCount = Environment.TickCount; 413 outgoingPacket.TickCount = Environment.TickCount;
@@ -424,11 +416,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
424 416
425 int dataLength = buffer.DataLength; 417 int dataLength = buffer.DataLength;
426 418
427 // Keep appending ACKs until there is no room left in the packet or there are 419 // Keep appending ACKs until there is no room left in the buffer or there are
428 // no more ACKs to append 420 // no more ACKs to append
429 uint ackCount = 0; 421 uint ackCount = 0;
430 uint ack; 422 uint ack;
431 while (dataLength + 5 < buffer.Data.Length && client.PendingAcks.Dequeue(out ack)) 423 while (dataLength + 5 < buffer.Data.Length && udpClient.PendingAcks.Dequeue(out ack))
432 { 424 {
433 Utils.UIntToBytesBig(ack, buffer.Data, dataLength); 425 Utils.UIntToBytesBig(ack, buffer.Data, dataLength);
434 dataLength += 4; 426 dataLength += 4;
@@ -447,24 +439,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
447 439
448 #endregion ACK Appending 440 #endregion ACK Appending
449 441
442 #region Sequence Number Assignment
443
450 if (!isResend) 444 if (!isResend)
451 { 445 {
452 // Not a resend, assign a new sequence number 446 // Not a resend, assign a new sequence number
453 uint sequenceNumber = (uint)Interlocked.Increment(ref client.CurrentSequence); 447 uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence);
454 Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); 448 Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1);
455 outgoingPacket.SequenceNumber = sequenceNumber; 449 outgoingPacket.SequenceNumber = sequenceNumber;
456 450
457 if (isReliable) 451 if (isReliable)
458 { 452 {
459 // Add this packet to the list of ACK responses we are waiting on from the server 453 // Add this packet to the list of ACK responses we are waiting on from the server
460 client.NeedAcks.Add(outgoingPacket); 454 udpClient.NeedAcks.Add(outgoingPacket);
461 } 455 }
462 } 456 }
463 457
458 #endregion Sequence Number Assignment
459
464 // Stats tracking 460 // Stats tracking
465 Interlocked.Increment(ref client.PacketsSent); 461 Interlocked.Increment(ref udpClient.PacketsSent);
466 if (isReliable) 462 if (isReliable)
467 Interlocked.Add(ref client.UnackedBytes, outgoingPacket.Buffer.DataLength); 463 Interlocked.Add(ref udpClient.UnackedBytes, outgoingPacket.Buffer.DataLength);
468 464
469 // Put the UDP payload on the wire 465 // Put the UDP payload on the wire
470 AsyncBeginSend(buffer); 466 AsyncBeginSend(buffer);
@@ -473,10 +469,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
473 protected override void PacketReceived(UDPPacketBuffer buffer) 469 protected override void PacketReceived(UDPPacketBuffer buffer)
474 { 470 {
475 // Debugging/Profiling 471 // Debugging/Profiling
476 //try { Thread.CurrentThread.Name = "PacketReceived (" + scene.RegionName + ")"; } 472 //try { Thread.CurrentThread.Name = "PacketReceived (" + m_scene.RegionInfo.RegionName + ")"; }
477 //catch (Exception) { } 473 //catch (Exception) { }
478 474
479 LLUDPClient client = null; 475 LLUDPClient udpClient = null;
480 Packet packet = null; 476 Packet packet = null;
481 int packetEnd = buffer.DataLength - 1; 477 int packetEnd = buffer.DataLength - 1;
482 IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint; 478 IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
@@ -491,61 +487,59 @@ namespace OpenSim.Region.ClientStack.LindenUDP
491 } 487 }
492 catch (MalformedDataException) 488 catch (MalformedDataException)
493 { 489 {
494 m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet:\n{0}", 490 m_log.ErrorFormat("[LLUDPSERVER]: Malformed data, cannot parse packet from {0}:\n{1}",
495 Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)); 491 buffer.RemoteEndPoint, Utils.BytesToHexString(buffer.Data, buffer.DataLength, null));
496 } 492 }
497 493
498 // Fail-safe check 494 // Fail-safe check
499 if (packet == null) 495 if (packet == null)
500 { 496 {
501 m_log.Warn("[LLUDPSERVER]: Couldn't build a message from the incoming data"); 497 m_log.Warn("[LLUDPSERVER]: Couldn't build a message from incoming data " + buffer.DataLength +
498 " bytes long from " + buffer.RemoteEndPoint);
502 return; 499 return;
503 } 500 }
504 501
505 //Stats.RecvBytes += (ulong)buffer.DataLength;
506 //++Stats.RecvPackets;
507
508 #endregion Decoding 502 #endregion Decoding
509 503
510 #region UseCircuitCode Handling 504 #region Packet to Client Mapping
511 505
506 // UseCircuitCode handling
512 if (packet.Type == PacketType.UseCircuitCode) 507 if (packet.Type == PacketType.UseCircuitCode)
513 { 508 {
514 UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet; 509 AddNewClient((UseCircuitCodePacket)packet, (IPEndPoint)buffer.RemoteEndPoint);
515 IClientAPI newuser;
516 uint circuitCode = useCircuitCode.CircuitCode.Code;
517
518 // Check if the client is already established
519 if (!m_scene.ClientManager.TryGetClient(circuitCode, out newuser))
520 {
521 AddNewClient(useCircuitCode, (IPEndPoint)buffer.RemoteEndPoint);
522 }
523 } 510 }
524 511
525 // Determine which agent this packet came from 512 // Determine which agent this packet came from
526 if (!clients.TryGetValue(address, out client)) 513 IClientAPI client;
514 if (!m_scene.ClientManager.TryGetValue(address, out client) || !(client is LLClientView))
527 { 515 {
528 m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address + " in " + m_scene.RegionInfo.RegionName); 516 m_log.Warn("[LLUDPSERVER]: Received a " + packet.Type + " packet from an unrecognized source: " + address +
517 " in " + m_scene.RegionInfo.RegionName + ", currently tracking " + m_scene.ClientManager.Count + " clients");
529 return; 518 return;
530 } 519 }
531 520
532 #endregion UseCircuitCode Handling 521 udpClient = ((LLClientView)client).UDPClient;
522
523 if (!udpClient.IsConnected)
524 return;
525
526 #endregion Packet to Client Mapping
533 527
534 // Stats tracking 528 // Stats tracking
535 Interlocked.Increment(ref client.PacketsReceived); 529 Interlocked.Increment(ref udpClient.PacketsReceived);
536 530
537 #region ACK Receiving 531 #region ACK Receiving
538 532
539 int now = Environment.TickCount; 533 int now = Environment.TickCount;
540 client.TickLastPacketReceived = now; 534 udpClient.TickLastPacketReceived = now;
541 535
542 // Handle appended ACKs 536 // Handle appended ACKs
543 if (packet.Header.AppendedAcks && packet.Header.AckList != null) 537 if (packet.Header.AppendedAcks && packet.Header.AckList != null)
544 { 538 {
545 lock (client.NeedAcks.SyncRoot) 539 lock (udpClient.NeedAcks.SyncRoot)
546 { 540 {
547 for (int i = 0; i < packet.Header.AckList.Length; i++) 541 for (int i = 0; i < packet.Header.AckList.Length; i++)
548 AcknowledgePacket(client, packet.Header.AckList[i], now, packet.Header.Resent); 542 AcknowledgePacket(udpClient, packet.Header.AckList[i], now, packet.Header.Resent);
549 } 543 }
550 } 544 }
551 545
@@ -554,10 +548,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
554 { 548 {
555 PacketAckPacket ackPacket = (PacketAckPacket)packet; 549 PacketAckPacket ackPacket = (PacketAckPacket)packet;
556 550
557 lock (client.NeedAcks.SyncRoot) 551 lock (udpClient.NeedAcks.SyncRoot)
558 { 552 {
559 for (int i = 0; i < ackPacket.Packets.Length; i++) 553 for (int i = 0; i < ackPacket.Packets.Length; i++)
560 AcknowledgePacket(client, ackPacket.Packets[i].ID, now, packet.Header.Resent); 554 AcknowledgePacket(udpClient, ackPacket.Packets[i].ID, now, packet.Header.Resent);
561 } 555 }
562 } 556 }
563 557
@@ -566,27 +560,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
566 #region ACK Sending 560 #region ACK Sending
567 561
568 if (packet.Header.Reliable) 562 if (packet.Header.Reliable)
569 client.PendingAcks.Enqueue((uint)packet.Header.Sequence); 563 udpClient.PendingAcks.Enqueue(packet.Header.Sequence);
570 564
571 // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out, 565 // This is a somewhat odd sequence of steps to pull the client.BytesSinceLastACK value out,
572 // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove 566 // add the current received bytes to it, test if 2*MTU bytes have been sent, if so remove
573 // 2*MTU bytes from the value and send ACKs, and finally add the local value back to 567 // 2*MTU bytes from the value and send ACKs, and finally add the local value back to
574 // client.BytesSinceLastACK. Lockless thread safety 568 // client.BytesSinceLastACK. Lockless thread safety
575 int bytesSinceLastACK = Interlocked.Exchange(ref client.BytesSinceLastACK, 0); 569 int bytesSinceLastACK = Interlocked.Exchange(ref udpClient.BytesSinceLastACK, 0);
576 bytesSinceLastACK += buffer.DataLength; 570 bytesSinceLastACK += buffer.DataLength;
577 if (bytesSinceLastACK > Packet.MTU * 2) 571 if (bytesSinceLastACK > Packet.MTU * 2)
578 { 572 {
579 bytesSinceLastACK -= Packet.MTU * 2; 573 bytesSinceLastACK -= Packet.MTU * 2;
580 SendAcks(client); 574 SendAcks(udpClient);
581 } 575 }
582 Interlocked.Add(ref client.BytesSinceLastACK, bytesSinceLastACK); 576 Interlocked.Add(ref udpClient.BytesSinceLastACK, bytesSinceLastACK);
583 577
584 #endregion ACK Sending 578 #endregion ACK Sending
585 579
586 #region Incoming Packet Accounting 580 #region Incoming Packet Accounting
587 581
588 // Check the archive of received reliable packet IDs to see whether we already received this packet 582 // Check the archive of received reliable packet IDs to see whether we already received this packet
589 if (packet.Header.Reliable && !client.PacketArchive.TryEnqueue(packet.Header.Sequence)) 583 if (packet.Header.Reliable && !udpClient.PacketArchive.TryEnqueue(packet.Header.Sequence))
590 { 584 {
591 if (packet.Header.Resent) 585 if (packet.Header.Resent)
592 m_log.Debug("[LLUDPSERVER]: Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type); 586 m_log.Debug("[LLUDPSERVER]: Received a resend of already processed packet #" + packet.Header.Sequence + ", type: " + packet.Type);
@@ -603,7 +597,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
603 if (packet.Type != PacketType.PacketAck) 597 if (packet.Type != PacketType.PacketAck)
604 { 598 {
605 // Inbox insertion 599 // Inbox insertion
606 packetInbox.Enqueue(new IncomingPacket(client, packet)); 600 packetInbox.Enqueue(new IncomingPacket(udpClient, packet));
607 } 601 }
608 } 602 }
609 603
@@ -623,53 +617,59 @@ namespace OpenSim.Region.ClientStack.LindenUDP
623 617
624 private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint) 618 private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint)
625 { 619 {
626 //Slave regions don't accept new clients 620 UUID agentID = useCircuitCode.CircuitCode.ID;
621 UUID sessionID = useCircuitCode.CircuitCode.SessionID;
622 uint circuitCode = useCircuitCode.CircuitCode.Code;
623
627 if (m_scene.RegionStatus != RegionStatus.SlaveScene) 624 if (m_scene.RegionStatus != RegionStatus.SlaveScene)
628 { 625 {
629 AuthenticateResponse sessionInfo; 626 AuthenticateResponse sessionInfo;
630 bool isNewCircuit = !clients.ContainsKey(remoteEndPoint); 627 if (IsClientAuthorized(useCircuitCode, out sessionInfo))
631
632 if (!IsClientAuthorized(useCircuitCode, out sessionInfo))
633 { 628 {
634 m_log.WarnFormat( 629 AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo);
635 "[CONNECTION FAILURE]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}",
636 useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint);
637 return;
638 } 630 }
639 631 else
640 if (isNewCircuit)
641 { 632 {
642 UUID agentID = useCircuitCode.CircuitCode.ID; 633 // Don't create circuits for unauthorized clients
643 UUID sessionID = useCircuitCode.CircuitCode.SessionID; 634 m_log.WarnFormat(
644 uint circuitCode = useCircuitCode.CircuitCode.Code; 635 "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}",
645 636 useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint);
646 AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo);
647 } 637 }
648 } 638 }
639 else
640 {
641 // Slave regions don't accept new clients
642 m_log.Debug("[LLUDPSERVER]: Slave region " + m_scene.RegionInfo.RegionName + " ignoring UseCircuitCode packet");
643 }
649 } 644 }
650 645
651 private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) 646 private void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo)
652 { 647 {
653 // Create the LLUDPClient 648 // Create the LLUDPClient
654 LLUDPClient client = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint); 649 LLUDPClient udpClient = new LLUDPClient(this, m_throttleRates, m_throttle, circuitCode, agentID, remoteEndPoint);
655
656 // Create the LLClientView
657 LLClientView clientApi = new LLClientView(remoteEndPoint, m_scene, this, client, sessionInfo, agentID, sessionID, circuitCode);
658 clientApi.OnViewerEffect += m_scene.ClientManager.ViewerEffectHandler;
659 clientApi.OnLogout += LogoutHandler;
660 clientApi.OnConnectionClosed += RemoveClient;
661
662 // Start the IClientAPI
663 m_scene.ClientManager.Add(circuitCode, clientApi);
664 clientApi.Start();
665 650
666 // Give LLUDPClient a reference to IClientAPI 651 if (!m_scene.ClientManager.ContainsKey(agentID))
667 client.ClientAPI = clientApi; 652 {
653 // Create the LLClientView
654 LLClientView client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode);
655 client.OnLogout += LogoutHandler;
668 656
669 // Add the new client to our list of tracked clients 657 // Start the IClientAPI
670 clients.Add(agentID, client.RemoteEndPoint, client); 658 client.Start();
659 }
660 else
661 {
662 m_log.WarnFormat("[LLUDPSERVER]: Ignoring a repeated UseCircuitCode from {0} at {1} for circuit {2}",
663 udpClient.AgentID, remoteEndPoint, circuitCode);
664 }
665 }
671 666
672 m_log.DebugFormat("[LLUDPSERVER]: Added new client {0} to region {1}", agentID, m_scene.RegionInfo.RegionName); 667 private void RemoveClient(LLUDPClient udpClient)
668 {
669 // Remove this client from the scene
670 IClientAPI client;
671 if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client))
672 client.Close();
673 } 673 }
674 674
675 private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend) 675 private void AcknowledgePacket(LLUDPClient client, uint ack, int currentTime, bool fromResend)
@@ -747,20 +747,28 @@ namespace OpenSim.Region.ClientStack.LindenUDP
747 elapsed500MS = 0; 747 elapsed500MS = 0;
748 } 748 }
749 749
750 clients.ForEach( 750 m_scene.ClientManager.ForEach(
751 delegate(LLUDPClient client) 751 delegate(IClientAPI client)
752 { 752 {
753 if (client.DequeueOutgoing()) 753 if (client is LLClientView)
754 packetSent = true;
755 if (resendUnacked)
756 ResendUnacked(client);
757 if (sendAcks)
758 { 754 {
759 SendAcks(client); 755 LLUDPClient udpClient = ((LLClientView)client).UDPClient;
760 client.SendPacketStats(); 756
757 if (udpClient.IsConnected)
758 {
759 if (udpClient.DequeueOutgoing())
760 packetSent = true;
761 if (resendUnacked)
762 ResendUnacked(udpClient);
763 if (sendAcks)
764 {
765 SendAcks(udpClient);
766 udpClient.SendPacketStats();
767 }
768 if (sendPings)
769 SendPing(udpClient);
770 }
761 } 771 }
762 if (sendPings)
763 SendPing(client);
764 } 772 }
765 ); 773 );
766 774
@@ -773,38 +781,48 @@ namespace OpenSim.Region.ClientStack.LindenUDP
773 { 781 {
774 IncomingPacket incomingPacket = (IncomingPacket)state; 782 IncomingPacket incomingPacket = (IncomingPacket)state;
775 Packet packet = incomingPacket.Packet; 783 Packet packet = incomingPacket.Packet;
776 LLUDPClient client = incomingPacket.Client; 784 LLUDPClient udpClient = incomingPacket.Client;
785 IClientAPI client;
777 786
778 // Sanity check 787 // Sanity check
779 if (packet == null || client == null || client.ClientAPI == null) 788 if (packet == null || udpClient == null)
780 { 789 {
781 m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", Client=\"{1}\", Client.ClientAPI=\"{2}\"", 790 m_log.WarnFormat("[LLUDPSERVER]: Processing a packet with incomplete state. Packet=\"{0}\", UDPClient=\"{1}\"",
782 packet, client, (client != null) ? client.ClientAPI : null); 791 packet, udpClient);
783 } 792 }
784 793
785 try 794 // Make sure this client is still alive
786 { 795 if (m_scene.ClientManager.TryGetValue(udpClient.AgentID, out client))
787 // Process this packet
788 client.ClientAPI.ProcessInPacket(packet);
789 }
790 catch (ThreadAbortException)
791 { 796 {
792 // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down 797 try
793 m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server"); 798 {
794 Stop(); 799 // Process this packet
800 client.ProcessInPacket(packet);
801 }
802 catch (ThreadAbortException)
803 {
804 // If something is trying to abort the packet processing thread, take that as a hint that it's time to shut down
805 m_log.Info("[LLUDPSERVER]: Caught a thread abort, shutting down the LLUDP server");
806 Stop();
807 }
808 catch (Exception e)
809 {
810 // Don't let a failure in an individual client thread crash the whole sim.
811 m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", udpClient.AgentID, packet.Type);
812 m_log.Error(e.Message, e);
813 }
795 } 814 }
796 catch (Exception e) 815 else
797 { 816 {
798 // Don't let a failure in an individual client thread crash the whole sim. 817 m_log.DebugFormat("[LLUDPSERVER]: Dropping incoming {0} packet for dead client {1}", packet.Type, udpClient.AgentID);
799 m_log.ErrorFormat("[LLUDPSERVER]: Client packet handler for {0} for packet {1} threw an exception", client.AgentID, packet.Type);
800 m_log.Error(e.Message, e);
801 } 818 }
802 } 819 }
803 820
804 private void LogoutHandler(IClientAPI client) 821 private void LogoutHandler(IClientAPI client)
805 { 822 {
806 client.SendLogoutPacket(); 823 client.SendLogoutPacket();
807 RemoveClient(client); 824 if (client.IsActive)
825 RemoveClient(((LLClientView)client).UDPClient);
808 } 826 }
809 } 827 }
810} 828}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
index 43a68ec..d16837d 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/OpenSimUDPBase.cs
@@ -1,419 +1,137 @@
1//--------- Modified Version ------------------- 1/*
2///* 2 * Copyright (c) 2006, Clutch, Inc.
3// * Copyright (c) 2006, Clutch, Inc. 3 * Original Author: Jeff Cesnik
4// * Original Author: Jeff Cesnik 4 * All rights reserved.
5// * All rights reserved. 5 *
6// * 6 * - Redistribution and use in source and binary forms, with or without
7// * - Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met:
8// * modification, are permitted provided that the following conditions are met: 8 *
9// * 9 * - Redistributions of source code must retain the above copyright notice, this
10// * - Redistributions of source code must retain the above copyright notice, this 10 * list of conditions and the following disclaimer.
11// * list of conditions and the following disclaimer. 11 * - Neither the name of the openmetaverse.org nor the names
12// * - Neither the name of the openmetaverse.org nor the names 12 * of its contributors may be used to endorse or promote products derived from
13// * of its contributors may be used to endorse or promote products derived from 13 * this software without specific prior written permission.
14// * this software without specific prior written permission. 14 *
15// * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16// * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17// * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18// * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19// * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20// * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21// * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22// * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23// * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24// * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25// * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * POSSIBILITY OF SUCH DAMAGE.
26// * POSSIBILITY OF SUCH DAMAGE. 26 */
27// */
28 27
29using System; 28using System;
30using System.Net; 29using System.Net;
31using System.Net.Sockets; 30using System.Net.Sockets;
32using System.Threading; 31using System.Threading;
33using OpenMetaverse; 32using log4net;
34 33
35//namespace OpenSim.Region.ClientStack.LindenUDP 34namespace OpenMetaverse
36//{
37// /// <summary>
38// ///
39// /// </summary>
40// public abstract class OpenSimUDPBase
41// {
42// // these abstract methods must be implemented in a derived class to actually do
43// // something with the packets that are sent and received.
44// protected abstract void PacketReceived(UDPPacketBuffer buffer);
45// protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent);
46
47// // the port to listen on
48// internal int udpPort;
49
50// // the UDP socket
51// private Socket udpSocket;
52
53// // the ReaderWriterLock is used solely for the purposes of shutdown (Stop()).
54// // since there are potentially many "reader" threads in the internal .NET IOCP
55// // thread pool, this is a cheaper synchronization primitive than using
56// // a Mutex object. This allows many UDP socket "reads" concurrently - when
57// // Stop() is called, it attempts to obtain a writer lock which will then
58// // wait until all outstanding operations are completed before shutting down.
59// // this avoids the problem of closing the socket with outstanding operations
60// // and trying to catch the inevitable ObjectDisposedException.
61// private ReaderWriterLock rwLock = new ReaderWriterLock();
62
63// // number of outstanding operations. This is a reference count
64// // which we use to ensure that the threads exit cleanly. Note that
65// // we need this because the threads will potentially still need to process
66// // data even after the socket is closed.
67// private int rwOperationCount = 0;
68
69// // the all important shutdownFlag. This is synchronized through the ReaderWriterLock.
70// private volatile bool shutdownFlag = true;
71
72// // the remote endpoint to communicate with
73// protected IPEndPoint remoteEndPoint = null;
74
75
76// /// <summary>
77// /// Initialize the UDP packet handler in server mode
78// /// </summary>
79// /// <param name="port">Port to listening for incoming UDP packets on</param>
80// public OpenSimUDPBase(int port)
81// {
82// udpPort = port;
83// }
84
85// /// <summary>
86// /// Initialize the UDP packet handler in client mode
87// /// </summary>
88// /// <param name="endPoint">Remote UDP server to connect to</param>
89// public OpenSimUDPBase(IPEndPoint endPoint)
90// {
91// remoteEndPoint = endPoint;
92// udpPort = 0;
93// }
94
95// /// <summary>
96// ///
97// /// </summary>
98// public void Start()
99// {
100// if (shutdownFlag)
101// {
102// if (remoteEndPoint == null)
103// {
104// // Server mode
105
106// // create and bind the socket
107// IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort);
108// udpSocket = new Socket(
109// AddressFamily.InterNetwork,
110// SocketType.Dgram,
111// ProtocolType.Udp);
112// udpSocket.Bind(ipep);
113// }
114// else
115// {
116// // Client mode
117// IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort);
118// udpSocket = new Socket(
119// AddressFamily.InterNetwork,
120// SocketType.Dgram,
121// ProtocolType.Udp);
122// udpSocket.Bind(ipep);
123// //udpSocket.Connect(remoteEndPoint);
124// }
125
126// // we're not shutting down, we're starting up
127// shutdownFlag = false;
128
129// // kick off an async receive. The Start() method will return, the
130// // actual receives will occur asynchronously and will be caught in
131// // AsyncEndRecieve().
132// AsyncBeginReceive();
133// }
134// }
135
136// /// <summary>
137// ///
138// /// </summary>
139// public void Stop()
140// {
141// if (!shutdownFlag)
142// {
143// // wait indefinitely for a writer lock. Once this is called, the .NET runtime
144// // will deny any more reader locks, in effect blocking all other send/receive
145// // threads. Once we have the lock, we set shutdownFlag to inform the other
146// // threads that the socket is closed.
147// rwLock.AcquireWriterLock(-1);
148// shutdownFlag = true;
149// udpSocket.Close();
150// rwLock.ReleaseWriterLock();
151
152// // wait for any pending operations to complete on other
153// // threads before exiting.
154// const int FORCE_STOP = 100;
155// int i = 0;
156// while (rwOperationCount > 0 && i < FORCE_STOP)
157// {
158// Thread.Sleep(10);
159// ++i;
160// }
161
162// if (i >= FORCE_STOP)
163// {
164// Logger.Log("UDPBase.Stop() forced shutdown while waiting on pending operations",
165// Helpers.LogLevel.Warning);
166// }
167// }
168// }
169
170// /// <summary>
171// ///
172// /// </summary>
173// public bool IsRunning
174// {
175// get { return !shutdownFlag; }
176// }
177
178// private void AsyncBeginReceive()
179// {
180// // this method actually kicks off the async read on the socket.
181// // we aquire a reader lock here to ensure that no other thread
182// // is trying to set shutdownFlag and close the socket.
183// rwLock.AcquireReaderLock(-1);
184
185// if (!shutdownFlag)
186// {
187// // increment the count of pending operations
188// Interlocked.Increment(ref rwOperationCount);
189
190// // allocate a packet buffer
191// //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
192// UDPPacketBuffer buf = new UDPPacketBuffer();
193
194// try
195// {
196// // kick off an async read
197// udpSocket.BeginReceiveFrom(
198// //wrappedBuffer.Instance.Data,
199// buf.Data,
200// 0,
201// UDPPacketBuffer.BUFFER_SIZE,
202// SocketFlags.None,
203// //ref wrappedBuffer.Instance.RemoteEndPoint,
204// ref buf.RemoteEndPoint,
205// new AsyncCallback(AsyncEndReceive),
206// //wrappedBuffer);
207// buf);
208// }
209// catch (SocketException)
210// {
211// // something bad happened
212// //Logger.Log(
213// // "A SocketException occurred in UDPServer.AsyncBeginReceive()",
214// // Helpers.LogLevel.Error, se);
215
216// // an error occurred, therefore the operation is void. Decrement the reference count.
217// Interlocked.Decrement(ref rwOperationCount);
218// }
219// }
220
221// // we're done with the socket for now, release the reader lock.
222// rwLock.ReleaseReaderLock();
223// }
224
225// private void AsyncEndReceive(IAsyncResult iar)
226// {
227// // Asynchronous receive operations will complete here through the call
228// // to AsyncBeginReceive
229
230// // aquire a reader lock
231// rwLock.AcquireReaderLock(-1);
232
233// if (!shutdownFlag)
234// {
235// // get the buffer that was created in AsyncBeginReceive
236// // this is the received data
237// //WrappedObject<UDPPacketBuffer> wrappedBuffer = (WrappedObject<UDPPacketBuffer>)iar.AsyncState;
238// //UDPPacketBuffer buffer = wrappedBuffer.Instance;
239// UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
240
241// try
242// {
243// // get the length of data actually read from the socket, store it with the
244// // buffer
245// buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
246
247// // this operation is now complete, decrement the reference count
248// Interlocked.Decrement(ref rwOperationCount);
249
250// // we're done with the socket, release the reader lock
251// rwLock.ReleaseReaderLock();
252
253// // call the abstract method PacketReceived(), passing the buffer that
254// // has just been filled from the socket read.
255// PacketReceived(buffer);
256// }
257// catch (SocketException)
258// {
259// // an error occurred, therefore the operation is void. Decrement the reference count.
260// Interlocked.Decrement(ref rwOperationCount);
261
262// // we're done with the socket for now, release the reader lock.
263// rwLock.ReleaseReaderLock();
264// }
265// finally
266// {
267// // start another receive - this keeps the server going!
268// AsyncBeginReceive();
269
270// //wrappedBuffer.Dispose();
271// }
272// }
273// else
274// {
275// // nothing bad happened, but we are done with the operation
276// // decrement the reference count and release the reader lock
277// Interlocked.Decrement(ref rwOperationCount);
278// rwLock.ReleaseReaderLock();
279// }
280// }
281
282// public void AsyncBeginSend(UDPPacketBuffer buf)
283// {
284// rwLock.AcquireReaderLock(-1);
285
286// if (!shutdownFlag)
287// {
288// try
289// {
290// Interlocked.Increment(ref rwOperationCount);
291// udpSocket.BeginSendTo(
292// buf.Data,
293// 0,
294// buf.DataLength,
295// SocketFlags.None,
296// buf.RemoteEndPoint,
297// new AsyncCallback(AsyncEndSend),
298// buf);
299// }
300// catch (SocketException)
301// {
302// //Logger.Log(
303// // "A SocketException occurred in UDPServer.AsyncBeginSend()",
304// // Helpers.LogLevel.Error, se);
305// }
306// }
307
308// rwLock.ReleaseReaderLock();
309// }
310
311// private void AsyncEndSend(IAsyncResult iar)
312// {
313// rwLock.AcquireReaderLock(-1);
314
315// if (!shutdownFlag)
316// {
317// UDPPacketBuffer buffer = (UDPPacketBuffer)iar.AsyncState;
318
319// try
320// {
321// int bytesSent = udpSocket.EndSendTo(iar);
322
323// // note that call to the abstract PacketSent() method - we are passing the number
324// // of bytes sent in a separate parameter, since we can't use buffer.DataLength which
325// // is the number of bytes to send (or bytes received depending upon whether this
326// // buffer was part of a send or a receive).
327// PacketSent(buffer, bytesSent);
328// }
329// catch (SocketException)
330// {
331// //Logger.Log(
332// // "A SocketException occurred in UDPServer.AsyncEndSend()",
333// // Helpers.LogLevel.Error, se);
334// }
335// }
336
337// Interlocked.Decrement(ref rwOperationCount);
338// rwLock.ReleaseReaderLock();
339// }
340// }
341//}
342
343//--------- Original Version -------------------
344
345
346namespace OpenSim.Region.ClientStack.LindenUDP
347{ 35{
348 /// <summary> 36 /// <summary>
349 /// 37 /// Base UDP server
350 /// </summary> 38 /// </summary>
351 public abstract class OpenSimUDPBase 39 public abstract class OpenSimUDPBase
352 { 40 {
353 // these abstract methods must be implemented in a derived class to actually do 41 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
354 // something with the packets that are sent and received. 42
43 /// <summary>
44 /// This method is called when an incoming packet is received
45 /// </summary>
46 /// <param name="buffer">Incoming packet buffer</param>
355 protected abstract void PacketReceived(UDPPacketBuffer buffer); 47 protected abstract void PacketReceived(UDPPacketBuffer buffer);
48
49 /// <summary>
50 /// This method is called when an outgoing packet is sent
51 /// </summary>
52 /// <param name="buffer">Outgoing packet buffer</param>
53 /// <param name="bytesSent">Number of bytes written to the wire</param>
356 protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent); 54 protected abstract void PacketSent(UDPPacketBuffer buffer, int bytesSent);
357 55
358 // the port to listen on 56 /// <summary>UDP port to bind to in server mode</summary>
359 internal int udpPort; 57 protected int m_udpPort;
58
59 /// <summary>Local IP address to bind to in server mode</summary>
60 protected IPAddress m_localBindAddress;
360 61
361 // the UDP socket 62 /// <summary>UDP socket, used in either client or server mode</summary>
362 private Socket udpSocket; 63 private Socket m_udpSocket;
363 64
364 // the all important shutdownFlag. 65 /// <summary>Flag to process packets asynchronously or synchronously</summary>
365 private volatile bool shutdownFlag = true; 66 private bool m_asyncPacketHandling;
366 67
367 // the remote endpoint to communicate with 68 /// <summary>The all important shutdown flag</summary>
368 protected IPEndPoint remoteEndPoint = null; 69 private volatile bool m_shutdownFlag = true;
70
71 /// <summary>Returns true if the server is currently listening, otherwise false</summary>
72 public bool IsRunning { get { return !m_shutdownFlag; } }
369 73
370 /// <summary> 74 /// <summary>
371 /// Initialize the UDP packet handler in server mode 75 /// Default constructor
372 /// </summary> 76 /// </summary>
77 /// <param name="bindAddress">Local IP address to bind the server to</param>
373 /// <param name="port">Port to listening for incoming UDP packets on</param> 78 /// <param name="port">Port to listening for incoming UDP packets on</param>
374 public OpenSimUDPBase(int port) 79 public OpenSimUDPBase(IPAddress bindAddress, int port)
375 { 80 {
376 udpPort = port; 81 m_localBindAddress = bindAddress;
82 m_udpPort = port;
377 } 83 }
378 84
379 /// <summary> 85 /// <summary>
380 /// Initialize the UDP packet handler in client mode 86 /// Start the UDP server
381 /// </summary> 87 /// </summary>
382 /// <param name="endPoint">Remote UDP server to connect to</param> 88 /// <param name="recvBufferSize">The size of the receive buffer for
383 public OpenSimUDPBase(IPEndPoint endPoint) 89 /// the UDP socket. This value is passed up to the operating system
90 /// and used in the system networking stack. Use zero to leave this
91 /// value as the default</param>
92 /// <param name="asyncPacketHandling">Set this to true to start
93 /// receiving more packets while current packet handler callbacks are
94 /// still running. Setting this to false will complete each packet
95 /// callback before the next packet is processed</param>
96 /// <remarks>This method will attempt to set the SIO_UDP_CONNRESET flag
97 /// on the socket to get newer versions of Windows to behave in a sane
98 /// manner (not throwing an exception when the remote side resets the
99 /// connection). This call is ignored on Mono where the flag is not
100 /// necessary</remarks>
101 public void Start(int recvBufferSize, bool asyncPacketHandling)
384 { 102 {
385 remoteEndPoint = endPoint; 103 m_asyncPacketHandling = asyncPacketHandling;
386 udpPort = 0;
387 }
388 104
389 /// <summary> 105 if (m_shutdownFlag)
390 ///
391 /// </summary>
392 public void Start()
393 {
394 if (shutdownFlag)
395 { 106 {
396 const int SIO_UDP_CONNRESET = -1744830452; 107 const int SIO_UDP_CONNRESET = -1744830452;
397 108
398 IPEndPoint ipep = new IPEndPoint(Settings.BIND_ADDR, udpPort); 109 IPEndPoint ipep = new IPEndPoint(m_localBindAddress, m_udpPort);
399 udpSocket = new Socket( 110
111 m_udpSocket = new Socket(
400 AddressFamily.InterNetwork, 112 AddressFamily.InterNetwork,
401 SocketType.Dgram, 113 SocketType.Dgram,
402 ProtocolType.Udp); 114 ProtocolType.Udp);
115
403 try 116 try
404 { 117 {
405 // this udp socket flag is not supported under mono, 118 // This udp socket flag is not supported under mono,
406 // so we'll catch the exception and continue 119 // so we'll catch the exception and continue
407 udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null); 120 m_udpSocket.IOControl(SIO_UDP_CONNRESET, new byte[] { 0 }, null);
121 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag set");
408 } 122 }
409 catch (SocketException) 123 catch (SocketException)
410 { 124 {
411 Logger.DebugLog("UDP SIO_UDP_CONNRESET flag not supported on this platform"); 125 m_log.Debug("[UDPBASE]: SIO_UDP_CONNRESET flag not supported on this platform, ignoring");
412 } 126 }
413 udpSocket.Bind(ipep); 127
128 if (recvBufferSize != 0)
129 m_udpSocket.ReceiveBufferSize = recvBufferSize;
130
131 m_udpSocket.Bind(ipep);
414 132
415 // we're not shutting down, we're starting up 133 // we're not shutting down, we're starting up
416 shutdownFlag = false; 134 m_shutdownFlag = false;
417 135
418 // kick off an async receive. The Start() method will return, the 136 // kick off an async receive. The Start() method will return, the
419 // actual receives will occur asynchronously and will be caught in 137 // actual receives will occur asynchronously and will be caught in
@@ -423,41 +141,33 @@ namespace OpenSim.Region.ClientStack.LindenUDP
423 } 141 }
424 142
425 /// <summary> 143 /// <summary>
426 /// 144 /// Stops the UDP server
427 /// </summary> 145 /// </summary>
428 public void Stop() 146 public void Stop()
429 { 147 {
430 if (!shutdownFlag) 148 if (!m_shutdownFlag)
431 { 149 {
432 // wait indefinitely for a writer lock. Once this is called, the .NET runtime 150 // wait indefinitely for a writer lock. Once this is called, the .NET runtime
433 // will deny any more reader locks, in effect blocking all other send/receive 151 // will deny any more reader locks, in effect blocking all other send/receive
434 // threads. Once we have the lock, we set shutdownFlag to inform the other 152 // threads. Once we have the lock, we set shutdownFlag to inform the other
435 // threads that the socket is closed. 153 // threads that the socket is closed.
436 shutdownFlag = true; 154 m_shutdownFlag = true;
437 udpSocket.Close(); 155 m_udpSocket.Close();
438 } 156 }
439 } 157 }
440 158
441 /// <summary>
442 ///
443 /// </summary>
444 public bool IsRunning
445 {
446 get { return !shutdownFlag; }
447 }
448
449 private void AsyncBeginReceive() 159 private void AsyncBeginReceive()
450 { 160 {
451 // allocate a packet buffer 161 // allocate a packet buffer
452 //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut(); 162 //WrappedObject<UDPPacketBuffer> wrappedBuffer = Pool.CheckOut();
453 UDPPacketBuffer buf = new UDPPacketBuffer(); 163 UDPPacketBuffer buf = new UDPPacketBuffer();
454 164
455 if (!shutdownFlag) 165 if (!m_shutdownFlag)
456 { 166 {
457 try 167 try
458 { 168 {
459 // kick off an async read 169 // kick off an async read
460 udpSocket.BeginReceiveFrom( 170 m_udpSocket.BeginReceiveFrom(
461 //wrappedBuffer.Instance.Data, 171 //wrappedBuffer.Instance.Data,
462 buf.Data, 172 buf.Data,
463 0, 173 0,
@@ -472,13 +182,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
472 { 182 {
473 if (e.SocketErrorCode == SocketError.ConnectionReset) 183 if (e.SocketErrorCode == SocketError.ConnectionReset)
474 { 184 {
475 Logger.Log("SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + udpPort, Helpers.LogLevel.Error); 185 m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort);
476 bool salvaged = false; 186 bool salvaged = false;
477 while (!salvaged) 187 while (!salvaged)
478 { 188 {
479 try 189 try
480 { 190 {
481 udpSocket.BeginReceiveFrom( 191 m_udpSocket.BeginReceiveFrom(
482 //wrappedBuffer.Instance.Data, 192 //wrappedBuffer.Instance.Data,
483 buf.Data, 193 buf.Data,
484 0, 194 0,
@@ -494,7 +204,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
494 catch (ObjectDisposedException) { return; } 204 catch (ObjectDisposedException) { return; }
495 } 205 }
496 206
497 Logger.Log("Salvaged the UDP listener on port " + udpPort, Helpers.LogLevel.Info); 207 m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort);
498 } 208 }
499 } 209 }
500 catch (ObjectDisposedException) { } 210 catch (ObjectDisposedException) { }
@@ -505,10 +215,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP
505 { 215 {
506 // Asynchronous receive operations will complete here through the call 216 // Asynchronous receive operations will complete here through the call
507 // to AsyncBeginReceive 217 // to AsyncBeginReceive
508 if (!shutdownFlag) 218 if (!m_shutdownFlag)
509 { 219 {
510 // start another receive - this keeps the server going! 220 // Asynchronous mode will start another receive before the
511 AsyncBeginReceive(); 221 // callback for this packet is even fired. Very parallel :-)
222 if (m_asyncPacketHandling)
223 AsyncBeginReceive();
512 224
513 // get the buffer that was created in AsyncBeginReceive 225 // get the buffer that was created in AsyncBeginReceive
514 // this is the received data 226 // this is the received data
@@ -520,7 +232,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
520 { 232 {
521 // get the length of data actually read from the socket, store it with the 233 // get the length of data actually read from the socket, store it with the
522 // buffer 234 // buffer
523 buffer.DataLength = udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint); 235 buffer.DataLength = m_udpSocket.EndReceiveFrom(iar, ref buffer.RemoteEndPoint);
524 236
525 // call the abstract method PacketReceived(), passing the buffer that 237 // call the abstract method PacketReceived(), passing the buffer that
526 // has just been filled from the socket read. 238 // has just been filled from the socket read.
@@ -528,17 +240,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP
528 } 240 }
529 catch (SocketException) { } 241 catch (SocketException) { }
530 catch (ObjectDisposedException) { } 242 catch (ObjectDisposedException) { }
531 //finally { wrappedBuffer.Dispose(); } 243 finally
244 {
245 //wrappedBuffer.Dispose();
246
247 // Synchronous mode waits until the packet callback completes
248 // before starting the receive to fetch another packet
249 if (!m_asyncPacketHandling)
250 AsyncBeginReceive();
251 }
252
532 } 253 }
533 } 254 }
534 255
535 public void AsyncBeginSend(UDPPacketBuffer buf) 256 public void AsyncBeginSend(UDPPacketBuffer buf)
536 { 257 {
537 if (!shutdownFlag) 258 if (!m_shutdownFlag)
538 { 259 {
539 try 260 try
540 { 261 {
541 udpSocket.BeginSendTo( 262 m_udpSocket.BeginSendTo(
542 buf.Data, 263 buf.Data,
543 0, 264 0,
544 buf.DataLength, 265 buf.DataLength,
@@ -557,7 +278,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
557 try 278 try
558 { 279 {
559 UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState; 280 UDPPacketBuffer buf = (UDPPacketBuffer)result.AsyncState;
560 int bytesSent = udpSocket.EndSendTo(result); 281 int bytesSent = m_udpSocket.EndSendTo(result);
561 282
562 PacketSent(buf, bytesSent); 283 PacketSent(buf, bytesSent);
563 } 284 }
@@ -566,4 +287,3 @@ namespace OpenSim.Region.ClientStack.LindenUDP
566 } 287 }
567 } 288 }
568} 289}
569
diff --git a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
index 858a03c..008d827 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/ThrottleRates.cs
@@ -26,6 +26,7 @@
26 */ 26 */
27 27
28using System; 28using System;
29using OpenSim.Framework;
29using Nini.Config; 30using Nini.Config;
30 31
31namespace OpenSim.Region.ClientStack.LindenUDP 32namespace OpenSim.Region.ClientStack.LindenUDP
@@ -45,12 +46,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP
45 public int Wind; 46 public int Wind;
46 /// <summary>Drip rate for cloud packets</summary> 47 /// <summary>Drip rate for cloud packets</summary>
47 public int Cloud; 48 public int Cloud;
48 /// <summary>Drip rate for task (state and transaction) packets</summary> 49 /// <summary>Drip rate for task packets</summary>
49 public int Task; 50 public int Task;
50 /// <summary>Drip rate for texture packets</summary> 51 /// <summary>Drip rate for texture packets</summary>
51 public int Texture; 52 public int Texture;
52 /// <summary>Drip rate for asset packets</summary> 53 /// <summary>Drip rate for asset packets</summary>
53 public int Asset; 54 public int Asset;
55 /// <summary>Drip rate for state packets</summary>
56 public int State;
57 /// <summary>Drip rate for the parent token bucket</summary>
58 public int Total;
54 59
55 /// <summary>Maximum burst rate for resent packets</summary> 60 /// <summary>Maximum burst rate for resent packets</summary>
56 public int ResendLimit; 61 public int ResendLimit;
@@ -66,6 +71,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
66 public int TextureLimit; 71 public int TextureLimit;
67 /// <summary>Maximum burst rate for asset packets</summary> 72 /// <summary>Maximum burst rate for asset packets</summary>
68 public int AssetLimit; 73 public int AssetLimit;
74 /// <summary>Maximum burst rate for state packets</summary>
75 public int StateLimit;
76 /// <summary>Burst rate for the parent token bucket</summary>
77 public int TotalLimit;
69 78
70 /// <summary> 79 /// <summary>
71 /// Default constructor 80 /// Default constructor
@@ -77,23 +86,81 @@ namespace OpenSim.Region.ClientStack.LindenUDP
77 { 86 {
78 IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"]; 87 IConfig throttleConfig = config.Configs["ClientStack.LindenUDP"];
79 88
80 Resend = throttleConfig.GetInt("ResendDefault", 12500); 89 Resend = throttleConfig.GetInt("resend_default", 12500);
81 Land = throttleConfig.GetInt("LandDefault", 500); 90 Land = throttleConfig.GetInt("land_default", 500);
82 Wind = throttleConfig.GetInt("WindDefault", 500); 91 Wind = throttleConfig.GetInt("wind_default", 500);
83 Cloud = throttleConfig.GetInt("CloudDefault", 500); 92 Cloud = throttleConfig.GetInt("cloud_default", 500);
84 Task = throttleConfig.GetInt("TaskDefault", 500); 93 Task = throttleConfig.GetInt("task_default", 500);
85 Texture = throttleConfig.GetInt("TextureDefault", 500); 94 Texture = throttleConfig.GetInt("texture_default", 500);
86 Asset = throttleConfig.GetInt("AssetDefault", 500); 95 Asset = throttleConfig.GetInt("asset_default", 500);
96 State = throttleConfig.GetInt("state_default", 500);
87 97
88 ResendLimit = throttleConfig.GetInt("ResendLimit", 18750); 98 Total = throttleConfig.GetInt("client_throttle_max_bps", 0);
89 LandLimit = throttleConfig.GetInt("LandLimit", 29750); 99
90 WindLimit = throttleConfig.GetInt("WindLimit", 18750); 100 ResendLimit = throttleConfig.GetInt("resend_limit", 18750);
91 CloudLimit = throttleConfig.GetInt("CloudLimit", 18750); 101 LandLimit = throttleConfig.GetInt("land_limit", 29750);
92 TaskLimit = throttleConfig.GetInt("TaskLimit", 55750); 102 WindLimit = throttleConfig.GetInt("wind_limit", 18750);
93 TextureLimit = throttleConfig.GetInt("TextureLimit", 55750); 103 CloudLimit = throttleConfig.GetInt("cloud_limit", 18750);
94 AssetLimit = throttleConfig.GetInt("AssetLimit", 27500); 104 TaskLimit = throttleConfig.GetInt("task_limit", 18750);
105 TextureLimit = throttleConfig.GetInt("texture_limit", 55750);
106 AssetLimit = throttleConfig.GetInt("asset_limit", 27500);
107 State = throttleConfig.GetInt("state_limit", 37000);
108
109 TotalLimit = throttleConfig.GetInt("client_throttle_max_bps", 0);
95 } 110 }
96 catch (Exception) { } 111 catch (Exception) { }
97 } 112 }
113
114 public int GetRate(ThrottleOutPacketType type)
115 {
116 switch (type)
117 {
118 case ThrottleOutPacketType.Resend:
119 return Resend;
120 case ThrottleOutPacketType.Land:
121 return Land;
122 case ThrottleOutPacketType.Wind:
123 return Wind;
124 case ThrottleOutPacketType.Cloud:
125 return Cloud;
126 case ThrottleOutPacketType.Task:
127 return Task;
128 case ThrottleOutPacketType.Texture:
129 return Texture;
130 case ThrottleOutPacketType.Asset:
131 return Asset;
132 case ThrottleOutPacketType.State:
133 return State;
134 case ThrottleOutPacketType.Unknown:
135 default:
136 return 0;
137 }
138 }
139
140 public int GetLimit(ThrottleOutPacketType type)
141 {
142 switch (type)
143 {
144 case ThrottleOutPacketType.Resend:
145 return ResendLimit;
146 case ThrottleOutPacketType.Land:
147 return LandLimit;
148 case ThrottleOutPacketType.Wind:
149 return WindLimit;
150 case ThrottleOutPacketType.Cloud:
151 return CloudLimit;
152 case ThrottleOutPacketType.Task:
153 return TaskLimit;
154 case ThrottleOutPacketType.Texture:
155 return TextureLimit;
156 case ThrottleOutPacketType.Asset:
157 return AssetLimit;
158 case ThrottleOutPacketType.State:
159 return StateLimit;
160 case ThrottleOutPacketType.Unknown:
161 default:
162 return 0;
163 }
164 }
98 } 165 }
99} 166}
diff --git a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
index 195ca57..f3242c1 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/UnackedPacketCollection.cs
@@ -103,6 +103,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
103 } 103 }
104 104
105 /// <summary> 105 /// <summary>
106 /// Removes all elements from the collection
107 /// </summary>
108 public void Clear()
109 {
110 lock (SyncRoot)
111 packets.Clear();
112 }
113
114 /// <summary>
106 /// Gets the packet with the lowest sequence number 115 /// Gets the packet with the lowest sequence number
107 /// </summary> 116 /// </summary>
108 /// <returns>The packet with the lowest sequence number, or null if the 117 /// <returns>The packet with the lowest sequence number, or null if the