diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs | 426 |
1 files changed, 426 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs new file mode 100644 index 0000000..49fd70a --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/PresenceModule.cs | |||
@@ -0,0 +1,426 @@ | |||
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 OpenSim 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 | using System; | ||
28 | using System.Collections; | ||
29 | using System.Collections.Generic; | ||
30 | using System.Reflection; | ||
31 | using System.Net; | ||
32 | using System.Threading; | ||
33 | using OpenMetaverse; | ||
34 | using log4net; | ||
35 | using Nini.Config; | ||
36 | using Nwc.XmlRpc; | ||
37 | using OpenSim.Framework; | ||
38 | using OpenSim.Framework.Client; | ||
39 | using OpenSim.Region.Framework.Interfaces; | ||
40 | using OpenSim.Region.Framework.Scenes; | ||
41 | |||
42 | namespace OpenSim.Region.CoreModules.Avatar.InstantMessage | ||
43 | { | ||
44 | public class PresenceModule : IRegionModule, IPresenceModule | ||
45 | { | ||
46 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
47 | |||
48 | private bool m_Enabled = false; | ||
49 | private bool m_Gridmode = false; | ||
50 | |||
51 | // some default scene for doing things that aren't connected to a specific scene. Avoids locking. | ||
52 | private Scene m_initialScene; | ||
53 | |||
54 | private List<Scene> m_Scenes = new List<Scene>(); | ||
55 | |||
56 | // we currently are only interested in root-agents. If the root isn't here, we don't know the region the | ||
57 | // user is in, so we have to ask the messaging server anyway. | ||
58 | private Dictionary<UUID, Scene> m_RootAgents = | ||
59 | new Dictionary<UUID, Scene>(); | ||
60 | |||
61 | public event PresenceChange OnPresenceChange; | ||
62 | public event BulkPresenceData OnBulkPresenceData; | ||
63 | |||
64 | public void Initialise(Scene scene, IConfigSource config) | ||
65 | { | ||
66 | lock (m_Scenes) | ||
67 | { | ||
68 | // This is a shared module; Initialise will be called for every region on this server. | ||
69 | // Only check config once for the first region. | ||
70 | if (m_Scenes.Count == 0) | ||
71 | { | ||
72 | IConfig cnf = config.Configs["Messaging"]; | ||
73 | if (cnf != null && cnf.GetString( | ||
74 | "PresenceModule", "PresenceModule") != | ||
75 | "PresenceModule") | ||
76 | return; | ||
77 | |||
78 | cnf = config.Configs["Startup"]; | ||
79 | if (cnf != null) | ||
80 | m_Gridmode = cnf.GetBoolean("gridmode", false); | ||
81 | |||
82 | m_Enabled = true; | ||
83 | |||
84 | m_initialScene = scene; | ||
85 | } | ||
86 | |||
87 | if (m_Gridmode) | ||
88 | NotifyMessageServerOfStartup(scene); | ||
89 | |||
90 | m_Scenes.Add(scene); | ||
91 | } | ||
92 | |||
93 | scene.RegisterModuleInterface<IPresenceModule>(this); | ||
94 | |||
95 | scene.EventManager.OnNewClient += OnNewClient; | ||
96 | scene.EventManager.OnSetRootAgentScene += OnSetRootAgentScene; | ||
97 | scene.EventManager.OnMakeChildAgent += OnMakeChildAgent; | ||
98 | } | ||
99 | |||
100 | public void PostInitialise() | ||
101 | { | ||
102 | } | ||
103 | |||
104 | public void Close() | ||
105 | { | ||
106 | if (!m_Gridmode || !m_Enabled) | ||
107 | return; | ||
108 | |||
109 | if (OnPresenceChange != null) | ||
110 | { | ||
111 | lock (m_RootAgents) | ||
112 | { | ||
113 | // on shutdown, users are kicked, too | ||
114 | foreach (KeyValuePair<UUID, Scene> pair in m_RootAgents) | ||
115 | { | ||
116 | OnPresenceChange(new PresenceInfo(pair.Key, UUID.Zero)); | ||
117 | } | ||
118 | } | ||
119 | } | ||
120 | |||
121 | lock (m_Scenes) | ||
122 | { | ||
123 | foreach (Scene scene in m_Scenes) | ||
124 | NotifyMessageServerOfShutdown(scene); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | public string Name | ||
129 | { | ||
130 | get { return "PresenceModule"; } | ||
131 | } | ||
132 | |||
133 | public bool IsSharedModule | ||
134 | { | ||
135 | get { return true; } | ||
136 | } | ||
137 | |||
138 | public void RequestBulkPresenceData(UUID[] users) | ||
139 | { | ||
140 | if (OnBulkPresenceData != null) | ||
141 | { | ||
142 | PresenceInfo[] result = new PresenceInfo[users.Length]; | ||
143 | if (m_Gridmode) | ||
144 | { | ||
145 | // first check the local information | ||
146 | List<UUID> uuids = new List<UUID>(); // the uuids to check remotely | ||
147 | List<int> indices = new List<int>(); // just for performance. | ||
148 | lock (m_RootAgents) | ||
149 | { | ||
150 | for (int i = 0; i < uuids.Count; ++i) | ||
151 | { | ||
152 | Scene scene; | ||
153 | if (m_RootAgents.TryGetValue(users[i], out scene)) | ||
154 | { | ||
155 | result[i] = new PresenceInfo(users[i], scene.RegionInfo.RegionID); | ||
156 | } | ||
157 | else | ||
158 | { | ||
159 | uuids.Add(users[i]); | ||
160 | indices.Add(i); | ||
161 | } | ||
162 | } | ||
163 | } | ||
164 | |||
165 | // now we have filtered out all the local root agents. The rest we have to request info about | ||
166 | Dictionary<UUID, FriendRegionInfo> infos = m_initialScene.GetFriendRegionInfos(uuids); | ||
167 | for (int i = 0; i < uuids.Count; ++i) | ||
168 | { | ||
169 | FriendRegionInfo info; | ||
170 | if (infos.TryGetValue(uuids[i], out info) && info.isOnline) | ||
171 | { | ||
172 | UUID regionID = info.regionID; | ||
173 | if (regionID == UUID.Zero) | ||
174 | { | ||
175 | // TODO this is the old messaging-server protocol; only the regionHandle is available. | ||
176 | // Fetch region-info to get the id | ||
177 | RegionInfo regionInfo = m_initialScene.RequestNeighbouringRegionInfo(info.regionHandle); | ||
178 | regionID = regionInfo.RegionID; | ||
179 | } | ||
180 | result[indices[i]] = new PresenceInfo(uuids[i], regionID); | ||
181 | } | ||
182 | else result[indices[i]] = new PresenceInfo(uuids[i], UUID.Zero); | ||
183 | } | ||
184 | } | ||
185 | else | ||
186 | { | ||
187 | // in standalone mode, we have all the info locally available. | ||
188 | lock (m_RootAgents) | ||
189 | { | ||
190 | for (int i = 0; i < users.Length; ++i) | ||
191 | { | ||
192 | Scene scene; | ||
193 | if (m_RootAgents.TryGetValue(users[i], out scene)) | ||
194 | { | ||
195 | result[i] = new PresenceInfo(users[i], scene.RegionInfo.RegionID); | ||
196 | } | ||
197 | else | ||
198 | { | ||
199 | result[i] = new PresenceInfo(users[i], UUID.Zero); | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | } | ||
204 | |||
205 | // tell everyone | ||
206 | OnBulkPresenceData(result); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | // new client doesn't mean necessarily that user logged in, it just means it entered one of the | ||
211 | // the regions on this server | ||
212 | public void OnNewClient(IClientAPI client) | ||
213 | { | ||
214 | client.OnConnectionClosed += OnConnectionClosed; | ||
215 | client.OnLogout += OnLogout; | ||
216 | |||
217 | // KLUDGE: See handler for details. | ||
218 | client.OnEconomyDataRequest += OnEconomyDataRequest; | ||
219 | } | ||
220 | |||
221 | // connection closed just means *one* client connection has been closed. It doesn't mean that the | ||
222 | // user has logged off; it might have just TPed away. | ||
223 | public void OnConnectionClosed(IClientAPI client) | ||
224 | { | ||
225 | // TODO: Have to think what we have to do here... | ||
226 | // Should we just remove the root from the list (if scene matches)? | ||
227 | if (!(client.Scene is Scene)) | ||
228 | return; | ||
229 | Scene scene = (Scene)client.Scene; | ||
230 | |||
231 | lock (m_RootAgents) | ||
232 | { | ||
233 | Scene rootScene; | ||
234 | if (!(m_RootAgents.TryGetValue(client.AgentId, out rootScene)) || scene != rootScene) | ||
235 | return; | ||
236 | |||
237 | m_RootAgents.Remove(client.AgentId); | ||
238 | } | ||
239 | |||
240 | // Should it have logged off, we'll do the logout part in OnLogout, even if no root is stored | ||
241 | // anymore. It logged off, after all... | ||
242 | } | ||
243 | |||
244 | // Triggered when the user logs off. | ||
245 | public void OnLogout(IClientAPI client) | ||
246 | { | ||
247 | if (!(client.Scene is Scene)) | ||
248 | return; | ||
249 | Scene scene = (Scene)client.Scene; | ||
250 | |||
251 | // On logout, we really remove the client from rootAgents, even if the scene doesn't match | ||
252 | lock (m_RootAgents) | ||
253 | { | ||
254 | if (m_RootAgents.ContainsKey(client.AgentId)) m_RootAgents.Remove(client.AgentId); | ||
255 | } | ||
256 | |||
257 | // now inform the messaging server and anyone who is interested | ||
258 | NotifyMessageServerOfAgentLeaving(client.AgentId, scene.RegionInfo.RegionID, scene.RegionInfo.RegionHandle); | ||
259 | if (OnPresenceChange != null) OnPresenceChange(new PresenceInfo(client.AgentId, UUID.Zero)); | ||
260 | } | ||
261 | |||
262 | public void OnSetRootAgentScene(UUID agentID, Scene scene) | ||
263 | { | ||
264 | // OnSetRootAgentScene can be called from several threads at once (with different agentID). | ||
265 | // Concurrent access to m_RootAgents is prone to failure on multi-core/-processor systems without | ||
266 | // correct locking). | ||
267 | lock (m_RootAgents) | ||
268 | { | ||
269 | Scene rootScene; | ||
270 | if (m_RootAgents.TryGetValue(agentID, out rootScene) && scene == rootScene) | ||
271 | { | ||
272 | return; | ||
273 | } | ||
274 | m_RootAgents[agentID] = scene; | ||
275 | } | ||
276 | // inform messaging server that agent changed the region | ||
277 | NotifyMessageServerOfAgentLocation(agentID, scene.RegionInfo.RegionID, scene.RegionInfo.RegionHandle); | ||
278 | } | ||
279 | |||
280 | private void OnEconomyDataRequest(UUID agentID) | ||
281 | { | ||
282 | // KLUDGE: This is the only way I found to get a message (only) after login was completed and the | ||
283 | // client is connected enough to receive UDP packets. | ||
284 | // This packet seems to be sent only once, just after connection was established to the first | ||
285 | // region after login. | ||
286 | // We use it here to trigger a presence update; the old update-on-login was never be heard by | ||
287 | // the freshly logged in viewer, as it wasn't connected to the region at that time. | ||
288 | // TODO: Feel free to replace this by a better solution if you find one. | ||
289 | |||
290 | // get the agent. This should work every time, as we just got a packet from it | ||
291 | ScenePresence agent = null; | ||
292 | lock (m_Scenes) | ||
293 | { | ||
294 | foreach (Scene scene in m_Scenes) | ||
295 | { | ||
296 | agent = scene.GetScenePresence(agentID); | ||
297 | if (agent != null) break; | ||
298 | } | ||
299 | } | ||
300 | |||
301 | // just to be paranoid... | ||
302 | if (agent == null) | ||
303 | { | ||
304 | m_log.ErrorFormat("[PRESENCE]: Got a packet from agent {0} who can't be found anymore!?", agentID); | ||
305 | return; | ||
306 | } | ||
307 | |||
308 | // we are a bit premature here, but the next packet will switch this child agent to root. | ||
309 | if (OnPresenceChange != null) OnPresenceChange(new PresenceInfo(agentID, agent.Scene.RegionInfo.RegionID)); | ||
310 | } | ||
311 | |||
312 | public void OnMakeChildAgent(ScenePresence agent) | ||
313 | { | ||
314 | // OnMakeChildAgent can be called from several threads at once (with different agent). | ||
315 | // Concurrent access to m_RootAgents is prone to failure on multi-core/-processor systems without | ||
316 | // correct locking). | ||
317 | lock (m_RootAgents) | ||
318 | { | ||
319 | Scene rootScene; | ||
320 | if (m_RootAgents.TryGetValue(agent.UUID, out rootScene) && agent.Scene == rootScene) | ||
321 | { | ||
322 | m_RootAgents.Remove(agent.UUID); | ||
323 | } | ||
324 | } | ||
325 | // don't notify the messaging-server; either this agent just had been downgraded and another one will be upgraded | ||
326 | // to root momentarily (which will notify the messaging-server), or possibly it will be closed in a moment, | ||
327 | // which will update the messaging-server, too. | ||
328 | } | ||
329 | |||
330 | private void NotifyMessageServerOfStartup(Scene scene) | ||
331 | { | ||
332 | Hashtable xmlrpcdata = new Hashtable(); | ||
333 | xmlrpcdata["RegionUUID"] = scene.RegionInfo.RegionID.ToString(); | ||
334 | ArrayList SendParams = new ArrayList(); | ||
335 | SendParams.Add(xmlrpcdata); | ||
336 | try | ||
337 | { | ||
338 | XmlRpcRequest UpRequest = new XmlRpcRequest("region_startup", SendParams); | ||
339 | XmlRpcResponse resp = UpRequest.Send(scene.CommsManager.NetworkServersInfo.MessagingURL, 5000); | ||
340 | |||
341 | Hashtable responseData = (Hashtable)resp.Value; | ||
342 | if (responseData == null || (!responseData.ContainsKey("success")) || (string)responseData["success"] != "TRUE") | ||
343 | { | ||
344 | m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of region startup for region {0}", scene.RegionInfo.RegionName); | ||
345 | } | ||
346 | } | ||
347 | catch (System.Net.WebException) | ||
348 | { | ||
349 | m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of region startup for region {0}", scene.RegionInfo.RegionName); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | private void NotifyMessageServerOfShutdown(Scene scene) | ||
354 | { | ||
355 | Hashtable xmlrpcdata = new Hashtable(); | ||
356 | xmlrpcdata["RegionUUID"] = scene.RegionInfo.RegionID.ToString(); | ||
357 | ArrayList SendParams = new ArrayList(); | ||
358 | SendParams.Add(xmlrpcdata); | ||
359 | try | ||
360 | { | ||
361 | XmlRpcRequest DownRequest = new XmlRpcRequest("region_shutdown", SendParams); | ||
362 | XmlRpcResponse resp = DownRequest.Send(scene.CommsManager.NetworkServersInfo.MessagingURL, 5000); | ||
363 | |||
364 | Hashtable responseData = (Hashtable)resp.Value; | ||
365 | if ((!responseData.ContainsKey("success")) || (string)responseData["success"] != "TRUE") | ||
366 | { | ||
367 | m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of region shutdown for region {0}", scene.RegionInfo.RegionName); | ||
368 | } | ||
369 | } | ||
370 | catch (System.Net.WebException) | ||
371 | { | ||
372 | m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of region shutdown for region {0}", scene.RegionInfo.RegionName); | ||
373 | } | ||
374 | } | ||
375 | |||
376 | private void NotifyMessageServerOfAgentLocation(UUID agentID, UUID region, ulong regionHandle) | ||
377 | { | ||
378 | Hashtable xmlrpcdata = new Hashtable(); | ||
379 | xmlrpcdata["AgentID"] = agentID.ToString(); | ||
380 | xmlrpcdata["RegionUUID"] = region.ToString(); | ||
381 | xmlrpcdata["RegionHandle"] = regionHandle.ToString(); | ||
382 | ArrayList SendParams = new ArrayList(); | ||
383 | SendParams.Add(xmlrpcdata); | ||
384 | try | ||
385 | { | ||
386 | XmlRpcRequest LocationRequest = new XmlRpcRequest("agent_location", SendParams); | ||
387 | XmlRpcResponse resp = LocationRequest.Send(m_Scenes[0].CommsManager.NetworkServersInfo.MessagingURL, 5000); | ||
388 | |||
389 | Hashtable responseData = (Hashtable)resp.Value; | ||
390 | if ((!responseData.ContainsKey("success")) || (string)responseData["success"] != "TRUE") | ||
391 | { | ||
392 | m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of agent location for {0}", agentID.ToString()); | ||
393 | } | ||
394 | } | ||
395 | catch (System.Net.WebException) | ||
396 | { | ||
397 | m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of agent location for {0}", agentID.ToString()); | ||
398 | } | ||
399 | } | ||
400 | |||
401 | private void NotifyMessageServerOfAgentLeaving(UUID agentID, UUID region, ulong regionHandle) | ||
402 | { | ||
403 | Hashtable xmlrpcdata = new Hashtable(); | ||
404 | xmlrpcdata["AgentID"] = agentID.ToString(); | ||
405 | xmlrpcdata["RegionUUID"] = region.ToString(); | ||
406 | xmlrpcdata["RegionHandle"] = regionHandle.ToString(); | ||
407 | ArrayList SendParams = new ArrayList(); | ||
408 | SendParams.Add(xmlrpcdata); | ||
409 | try | ||
410 | { | ||
411 | XmlRpcRequest LeavingRequest = new XmlRpcRequest("agent_leaving", SendParams); | ||
412 | XmlRpcResponse resp = LeavingRequest.Send(m_Scenes[0].CommsManager.NetworkServersInfo.MessagingURL, 5000); | ||
413 | |||
414 | Hashtable responseData = (Hashtable)resp.Value; | ||
415 | if ((!responseData.ContainsKey("success")) || (string)responseData["success"] != "TRUE") | ||
416 | { | ||
417 | m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of agent leaving for {0}", agentID.ToString()); | ||
418 | } | ||
419 | } | ||
420 | catch (System.Net.WebException) | ||
421 | { | ||
422 | m_log.ErrorFormat("[PRESENCE]: Failed to notify message server of agent leaving for {0}", agentID.ToString()); | ||
423 | } | ||
424 | } | ||
425 | } | ||
426 | } | ||