diff options
Diffstat (limited to '')
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; | |||
31 | using OpenMetaverse.Imaging; | 31 | using OpenMetaverse.Imaging; |
32 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
33 | using OpenSim.Region.Framework.Interfaces; | 33 | using OpenSim.Region.Framework.Interfaces; |
34 | using OpenSim.Region.Framework.Scenes.Hypergrid; | ||
34 | using OpenSim.Services.Interfaces; | 35 | using OpenSim.Services.Interfaces; |
35 | using log4net; | 36 | using log4net; |
36 | using System.Reflection; | 37 | using 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; | |||
43 | using OpenSim.Framework.Statistics; | 43 | using OpenSim.Framework.Statistics; |
44 | using OpenSim.Region.Framework.Interfaces; | 44 | using OpenSim.Region.Framework.Interfaces; |
45 | using OpenSim.Region.Framework.Scenes; | 45 | using OpenSim.Region.Framework.Scenes; |
46 | using OpenSim.Region.Framework.Scenes.Hypergrid; | ||
46 | using OpenSim.Services.Interfaces; | 47 | using OpenSim.Services.Interfaces; |
47 | using Timer=System.Timers.Timer; | 48 | using Timer=System.Timers.Timer; |
48 | using AssetLandmark = OpenSim.Framework.AssetLandmark; | 49 | using 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 @@ | |||
28 | using System; | 28 | using System; |
29 | using System.Collections.Generic; | 29 | using System.Collections.Generic; |
30 | using System.Net; | 30 | using System.Net; |
31 | using log4net; | ||
31 | using OpenSim.Framework; | 32 | using OpenSim.Framework; |
32 | using OpenMetaverse; | 33 | using OpenMetaverse; |
34 | using OpenMetaverse.Packets; | ||
33 | 35 | ||
34 | namespace OpenSim.Region.ClientStack.LindenUDP | 36 | namespace 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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Net; | ||
31 | using OpenSim.Framework; | ||
32 | using OpenMetaverse; | ||
33 | |||
34 | using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; | ||
35 | |||
36 | namespace 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 | ||
29 | using System; | 28 | using System; |
30 | using System.Net; | 29 | using System.Net; |
31 | using System.Net.Sockets; | 30 | using System.Net.Sockets; |
32 | using System.Threading; | 31 | using System.Threading; |
33 | using OpenMetaverse; | 32 | using log4net; |
34 | 33 | ||
35 | //namespace OpenSim.Region.ClientStack.LindenUDP | 34 | namespace 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 | |||
346 | namespace 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 | ||
28 | using System; | 28 | using System; |
29 | using OpenSim.Framework; | ||
29 | using Nini.Config; | 30 | using Nini.Config; |
30 | 31 | ||
31 | namespace OpenSim.Region.ClientStack.LindenUDP | 32 | namespace 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 |