clockwerk-opensim-stable – Blame information for rev 1

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