diff options
author | UbitUmarov | 2015-09-01 11:43:07 +0100 |
---|---|---|
committer | UbitUmarov | 2015-09-01 11:43:07 +0100 |
commit | fb78b182520fc9bb0f971afd0322029c70278ea6 (patch) | |
tree | b4e30d383938fdeef8c92d1d1c2f44bb61d329bd /OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs | |
parent | lixo (diff) | |
parent | Mantis #7713: fixed bug introduced by 1st MOSES patch. (diff) | |
download | opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.zip opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.gz opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.bz2 opensim-SC-fb78b182520fc9bb0f971afd0322029c70278ea6.tar.xz |
Merge remote-tracking branch 'os/master'
Diffstat (limited to 'OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs')
-rw-r--r-- | OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs new file mode 100644 index 0000000..a9d2de0 --- /dev/null +++ b/OpenSim/Region/CoreModules/Avatar/Chat/ChatModule.cs | |||
@@ -0,0 +1,395 @@ | |||
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.Reflection; | ||
31 | using log4net; | ||
32 | using Nini.Config; | ||
33 | using Mono.Addins; | ||
34 | using OpenMetaverse; | ||
35 | using OpenMetaverse.StructuredData; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Region.Framework.Interfaces; | ||
38 | using OpenSim.Region.Framework.Scenes; | ||
39 | |||
40 | namespace OpenSim.Region.CoreModules.Avatar.Chat | ||
41 | { | ||
42 | [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "ChatModule")] | ||
43 | public class ChatModule : ISharedRegionModule | ||
44 | { | ||
45 | private static readonly ILog m_log = | ||
46 | LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
47 | |||
48 | private const int DEBUG_CHANNEL = 2147483647; | ||
49 | |||
50 | private bool m_enabled = true; | ||
51 | private int m_saydistance = 20; | ||
52 | private int m_shoutdistance = 100; | ||
53 | private int m_whisperdistance = 10; | ||
54 | |||
55 | internal object m_syncy = new object(); | ||
56 | |||
57 | internal IConfig m_config; | ||
58 | |||
59 | #region ISharedRegionModule Members | ||
60 | public virtual void Initialise(IConfigSource config) | ||
61 | { | ||
62 | m_config = config.Configs["Chat"]; | ||
63 | |||
64 | if (null == m_config) | ||
65 | { | ||
66 | m_log.Info("[CHAT]: no config found, plugin disabled"); | ||
67 | m_enabled = false; | ||
68 | return; | ||
69 | } | ||
70 | |||
71 | if (!m_config.GetBoolean("enabled", true)) | ||
72 | { | ||
73 | m_log.Info("[CHAT]: plugin disabled by configuration"); | ||
74 | m_enabled = false; | ||
75 | return; | ||
76 | } | ||
77 | |||
78 | m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance); | ||
79 | m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance); | ||
80 | m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance); | ||
81 | } | ||
82 | |||
83 | public virtual void AddRegion(Scene scene) | ||
84 | { | ||
85 | if (!m_enabled) | ||
86 | return; | ||
87 | |||
88 | scene.EventManager.OnNewClient += OnNewClient; | ||
89 | scene.EventManager.OnChatFromWorld += OnChatFromWorld; | ||
90 | scene.EventManager.OnChatBroadcast += OnChatBroadcast; | ||
91 | |||
92 | m_log.InfoFormat("[CHAT]: Initialized for {0} w:{1} s:{2} S:{3}", scene.RegionInfo.RegionName, | ||
93 | m_whisperdistance, m_saydistance, m_shoutdistance); | ||
94 | } | ||
95 | |||
96 | public virtual void RegionLoaded(Scene scene) | ||
97 | { | ||
98 | if (!m_enabled) | ||
99 | return; | ||
100 | |||
101 | ISimulatorFeaturesModule featuresModule = scene.RequestModuleInterface<ISimulatorFeaturesModule>(); | ||
102 | |||
103 | if (featuresModule != null) | ||
104 | featuresModule.OnSimulatorFeaturesRequest += OnSimulatorFeaturesRequest; | ||
105 | |||
106 | } | ||
107 | |||
108 | public virtual void RemoveRegion(Scene scene) | ||
109 | { | ||
110 | if (!m_enabled) | ||
111 | return; | ||
112 | |||
113 | scene.EventManager.OnNewClient -= OnNewClient; | ||
114 | scene.EventManager.OnChatFromWorld -= OnChatFromWorld; | ||
115 | scene.EventManager.OnChatBroadcast -= OnChatBroadcast; | ||
116 | } | ||
117 | |||
118 | public virtual void Close() | ||
119 | { | ||
120 | } | ||
121 | |||
122 | public virtual void PostInitialise() | ||
123 | { | ||
124 | } | ||
125 | |||
126 | public Type ReplaceableInterface | ||
127 | { | ||
128 | get { return null; } | ||
129 | } | ||
130 | |||
131 | public virtual string Name | ||
132 | { | ||
133 | get { return "ChatModule"; } | ||
134 | } | ||
135 | |||
136 | #endregion | ||
137 | |||
138 | |||
139 | public virtual void OnNewClient(IClientAPI client) | ||
140 | { | ||
141 | client.OnChatFromClient += OnChatFromClient; | ||
142 | } | ||
143 | |||
144 | protected OSChatMessage FixPositionOfChatMessage(OSChatMessage c) | ||
145 | { | ||
146 | ScenePresence avatar; | ||
147 | Scene scene = (Scene)c.Scene; | ||
148 | if ((avatar = scene.GetScenePresence(c.Sender.AgentId)) != null) | ||
149 | c.Position = avatar.AbsolutePosition; | ||
150 | |||
151 | return c; | ||
152 | } | ||
153 | |||
154 | public virtual void OnChatFromClient(Object sender, OSChatMessage c) | ||
155 | { | ||
156 | c = FixPositionOfChatMessage(c); | ||
157 | |||
158 | // redistribute to interested subscribers | ||
159 | Scene scene = (Scene)c.Scene; | ||
160 | scene.EventManager.TriggerOnChatFromClient(sender, c); | ||
161 | |||
162 | // early return if not on public or debug channel | ||
163 | if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL) return; | ||
164 | |||
165 | // sanity check: | ||
166 | if (c.Sender == null) | ||
167 | { | ||
168 | m_log.ErrorFormat("[CHAT]: OnChatFromClient from {0} has empty Sender field!", sender); | ||
169 | return; | ||
170 | } | ||
171 | |||
172 | DeliverChatToAvatars(ChatSourceType.Agent, c); | ||
173 | } | ||
174 | |||
175 | public virtual void OnChatFromWorld(Object sender, OSChatMessage c) | ||
176 | { | ||
177 | // early return if not on public or debug channel | ||
178 | if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL) return; | ||
179 | |||
180 | DeliverChatToAvatars(ChatSourceType.Object, c); | ||
181 | } | ||
182 | |||
183 | protected virtual void DeliverChatToAvatars(ChatSourceType sourceType, OSChatMessage c) | ||
184 | { | ||
185 | string fromName = c.From; | ||
186 | UUID fromID = UUID.Zero; | ||
187 | UUID ownerID = UUID.Zero; | ||
188 | UUID targetID = c.TargetUUID; | ||
189 | string message = c.Message; | ||
190 | Scene scene = (Scene)c.Scene; | ||
191 | Vector3 fromPos = c.Position; | ||
192 | Vector3 regionPos = new Vector3(scene.RegionInfo.WorldLocX, scene.RegionInfo.WorldLocY, 0); | ||
193 | |||
194 | if (c.Channel == DEBUG_CHANNEL) c.Type = ChatTypeEnum.DebugChannel; | ||
195 | |||
196 | switch (sourceType) | ||
197 | { | ||
198 | case ChatSourceType.Agent: | ||
199 | ScenePresence avatar = scene.GetScenePresence(c.Sender.AgentId); | ||
200 | fromPos = avatar.AbsolutePosition; | ||
201 | fromName = avatar.Name; | ||
202 | fromID = c.Sender.AgentId; | ||
203 | ownerID = c.Sender.AgentId; | ||
204 | |||
205 | break; | ||
206 | |||
207 | case ChatSourceType.Object: | ||
208 | fromID = c.SenderUUID; | ||
209 | |||
210 | if (c.SenderObject != null && c.SenderObject is SceneObjectPart) | ||
211 | ownerID = ((SceneObjectPart)c.SenderObject).OwnerID; | ||
212 | |||
213 | break; | ||
214 | } | ||
215 | |||
216 | // TODO: iterate over message | ||
217 | if (message.Length >= 1000) // libomv limit | ||
218 | message = message.Substring(0, 1000); | ||
219 | |||
220 | // m_log.DebugFormat( | ||
221 | // "[CHAT]: DCTA: fromID {0} fromName {1}, region{2}, cType {3}, sType {4}, targetID {5}", | ||
222 | // fromID, fromName, scene.RegionInfo.RegionName, c.Type, sourceType, targetID); | ||
223 | |||
224 | HashSet<UUID> receiverIDs = new HashSet<UUID>(); | ||
225 | |||
226 | if (targetID == UUID.Zero) | ||
227 | { | ||
228 | // This should use ForEachClient, but clients don't have a position. | ||
229 | // If camera is moved into client, then camera position can be used | ||
230 | scene.ForEachScenePresence( | ||
231 | delegate(ScenePresence presence) | ||
232 | { | ||
233 | if (TrySendChatMessage( | ||
234 | presence, fromPos, regionPos, fromID, ownerID, fromName, c.Type, message, sourceType, false)) | ||
235 | receiverIDs.Add(presence.UUID); | ||
236 | } | ||
237 | ); | ||
238 | } | ||
239 | else | ||
240 | { | ||
241 | // This is a send to a specific client eg from llRegionSayTo | ||
242 | // no need to check distance etc, jand send is as say | ||
243 | ScenePresence presence = scene.GetScenePresence(targetID); | ||
244 | if (presence != null && !presence.IsChildAgent) | ||
245 | { | ||
246 | if (TrySendChatMessage( | ||
247 | presence, fromPos, regionPos, fromID, ownerID, fromName, ChatTypeEnum.Say, message, sourceType, true)) | ||
248 | receiverIDs.Add(presence.UUID); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | scene.EventManager.TriggerOnChatToClients( | ||
253 | fromID, receiverIDs, message, c.Type, fromPos, fromName, sourceType, ChatAudibleLevel.Fully); | ||
254 | } | ||
255 | |||
256 | static private Vector3 CenterOfRegion = new Vector3(128, 128, 30); | ||
257 | |||
258 | public virtual void OnChatBroadcast(Object sender, OSChatMessage c) | ||
259 | { | ||
260 | if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL) return; | ||
261 | |||
262 | ChatTypeEnum cType = c.Type; | ||
263 | if (c.Channel == DEBUG_CHANNEL) | ||
264 | cType = ChatTypeEnum.DebugChannel; | ||
265 | |||
266 | if (cType == ChatTypeEnum.Region) | ||
267 | cType = ChatTypeEnum.Say; | ||
268 | |||
269 | if (c.Message.Length > 1100) | ||
270 | c.Message = c.Message.Substring(0, 1000); | ||
271 | |||
272 | // broadcast chat works by redistributing every incoming chat | ||
273 | // message to each avatar in the scene. | ||
274 | string fromName = c.From; | ||
275 | |||
276 | UUID fromID = UUID.Zero; | ||
277 | UUID ownerID = UUID.Zero; | ||
278 | ChatSourceType sourceType = ChatSourceType.Object; | ||
279 | if (null != c.Sender) | ||
280 | { | ||
281 | ScenePresence avatar = (c.Scene as Scene).GetScenePresence(c.Sender.AgentId); | ||
282 | fromID = c.Sender.AgentId; | ||
283 | fromName = avatar.Name; | ||
284 | ownerID = c.Sender.AgentId; | ||
285 | sourceType = ChatSourceType.Agent; | ||
286 | } | ||
287 | else if (c.SenderUUID != UUID.Zero) | ||
288 | { | ||
289 | fromID = c.SenderUUID; | ||
290 | ownerID = ((SceneObjectPart)c.SenderObject).OwnerID; | ||
291 | } | ||
292 | |||
293 | // m_log.DebugFormat("[CHAT] Broadcast: fromID {0} fromName {1}, cType {2}, sType {3}", fromID, fromName, cType, sourceType); | ||
294 | |||
295 | HashSet<UUID> receiverIDs = new HashSet<UUID>(); | ||
296 | |||
297 | ((Scene)c.Scene).ForEachRootClient( | ||
298 | delegate(IClientAPI client) | ||
299 | { | ||
300 | // don't forward SayOwner chat from objects to | ||
301 | // non-owner agents | ||
302 | if ((c.Type == ChatTypeEnum.Owner) && | ||
303 | (null != c.SenderObject) && | ||
304 | (((SceneObjectPart)c.SenderObject).OwnerID != client.AgentId)) | ||
305 | return; | ||
306 | |||
307 | client.SendChatMessage( | ||
308 | c.Message, (byte)cType, CenterOfRegion, fromName, fromID, ownerID, | ||
309 | (byte)sourceType, (byte)ChatAudibleLevel.Fully); | ||
310 | |||
311 | receiverIDs.Add(client.AgentId); | ||
312 | }); | ||
313 | |||
314 | (c.Scene as Scene).EventManager.TriggerOnChatToClients( | ||
315 | fromID, receiverIDs, c.Message, cType, CenterOfRegion, fromName, sourceType, ChatAudibleLevel.Fully); | ||
316 | } | ||
317 | |||
318 | /// <summary> | ||
319 | /// Try to send a message to the given presence | ||
320 | /// </summary> | ||
321 | /// <param name="presence">The receiver</param> | ||
322 | /// <param name="fromPos"></param> | ||
323 | /// <param name="regionPos">/param> | ||
324 | /// <param name="fromAgentID"></param> | ||
325 | /// <param name='ownerID'> | ||
326 | /// Owner of the message. For at least some messages from objects, this has to be correctly filled with the owner's UUID. | ||
327 | /// This is the case for script error messages in viewer 3 since LLViewer change EXT-7762 | ||
328 | /// </param> | ||
329 | /// <param name="fromName"></param> | ||
330 | /// <param name="type"></param> | ||
331 | /// <param name="message"></param> | ||
332 | /// <param name="src"></param> | ||
333 | /// <returns>true if the message was sent to the receiver, false if it was not sent due to failing a | ||
334 | /// precondition</returns> | ||
335 | protected virtual bool TrySendChatMessage( | ||
336 | ScenePresence presence, Vector3 fromPos, Vector3 regionPos, | ||
337 | UUID fromAgentID, UUID ownerID, string fromName, ChatTypeEnum type, | ||
338 | string message, ChatSourceType src, bool ignoreDistance) | ||
339 | { | ||
340 | if (presence.LifecycleState != ScenePresenceState.Running) | ||
341 | return false; | ||
342 | |||
343 | if (!ignoreDistance) | ||
344 | { | ||
345 | Vector3 fromRegionPos = fromPos + regionPos; | ||
346 | Vector3 toRegionPos = presence.AbsolutePosition + | ||
347 | new Vector3(presence.Scene.RegionInfo.WorldLocX, presence.Scene.RegionInfo.WorldLocY, 0); | ||
348 | |||
349 | int dis = (int)Util.GetDistanceTo(toRegionPos, fromRegionPos); | ||
350 | |||
351 | if (type == ChatTypeEnum.Whisper && dis > m_whisperdistance || | ||
352 | type == ChatTypeEnum.Say && dis > m_saydistance || | ||
353 | type == ChatTypeEnum.Shout && dis > m_shoutdistance) | ||
354 | { | ||
355 | return false; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | // TODO: should change so the message is sent through the avatar rather than direct to the ClientView | ||
360 | presence.ControllingClient.SendChatMessage( | ||
361 | message, (byte) type, fromPos, fromName, | ||
362 | fromAgentID, ownerID, (byte)src, (byte)ChatAudibleLevel.Fully); | ||
363 | |||
364 | return true; | ||
365 | } | ||
366 | |||
367 | #region SimulatorFeaturesRequest | ||
368 | |||
369 | static OSDInteger m_SayRange, m_WhisperRange, m_ShoutRange; | ||
370 | |||
371 | private void OnSimulatorFeaturesRequest(UUID agentID, ref OSDMap features) | ||
372 | { | ||
373 | OSD extras = new OSDMap(); | ||
374 | if (features.ContainsKey("OpenSimExtras")) | ||
375 | extras = features["OpenSimExtras"]; | ||
376 | else | ||
377 | features["OpenSimExtras"] = extras; | ||
378 | |||
379 | if (m_SayRange == null) | ||
380 | { | ||
381 | // Do this only once | ||
382 | m_SayRange = new OSDInteger(m_saydistance); | ||
383 | m_WhisperRange = new OSDInteger(m_whisperdistance); | ||
384 | m_ShoutRange = new OSDInteger(m_shoutdistance); | ||
385 | } | ||
386 | |||
387 | ((OSDMap)extras)["say-range"] = m_SayRange; | ||
388 | ((OSDMap)extras)["whisper-range"] = m_WhisperRange; | ||
389 | ((OSDMap)extras)["shout-range"] = m_ShoutRange; | ||
390 | |||
391 | } | ||
392 | |||
393 | #endregion | ||
394 | } | ||
395 | } \ No newline at end of file | ||