aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs')
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs424
1 files changed, 424 insertions, 0 deletions
diff --git a/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs b/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs
new file mode 100644
index 0000000..6733120
--- /dev/null
+++ b/OpenSim/Region/OptionalModules/Avatar/Chat/RegionState.cs
@@ -0,0 +1,424 @@
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
28using System;
29using System.Collections.Generic;
30using System.Reflection;
31using System.Text.RegularExpressions;
32using log4net;
33using Nini.Config;
34using OpenSim.Framework;
35using OpenSim.Region.Framework.Interfaces;
36using OpenSim.Region.Framework.Scenes;
37
38namespace OpenSim.Region.OptionalModules.Avatar.Chat
39{
40 // An instance of this class exists for every active region
41
42 internal class RegionState
43 {
44
45 private static readonly ILog m_log =
46 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
47
48 private static readonly OpenMetaverse.Vector3 CenterOfRegion = new OpenMetaverse.Vector3(128, 128, 20);
49 private const int DEBUG_CHANNEL = 2147483647;
50
51 private static int _idk_ = 0;
52
53 // Runtime variables; these values are assigned when the
54 // IrcState is created and remain constant thereafter.
55
56 internal string Region = String.Empty;
57 internal string Host = String.Empty;
58 internal string LocX = String.Empty;
59 internal string LocY = String.Empty;
60 internal string MA1 = String.Empty;
61 internal string MA2 = String.Empty;
62 internal string IDK = String.Empty;
63
64 // System values - used only be the IRC classes themselves
65
66 internal ChannelState cs = null; // associated IRC configuration
67 internal Scene scene = null; // associated scene
68 internal IConfig config = null; // configuration file reference
69 internal bool enabled = true;
70
71 // This list is used to keep track of who is here, and by
72 // implication, who is not.
73
74 internal List<IClientAPI> clients = new List<IClientAPI>();
75
76 // Setup runtime variable values
77
78 public RegionState(Scene p_scene, IConfig p_config)
79 {
80
81 scene = p_scene;
82 config = p_config;
83
84 Region = scene.RegionInfo.RegionName;
85 Host = scene.RegionInfo.ExternalHostName;
86 LocX = Convert.ToString(scene.RegionInfo.RegionLocX);
87 LocY = Convert.ToString(scene.RegionInfo.RegionLocY);
88 MA1 = scene.RegionInfo.MasterAvatarFirstName;
89 MA2 = scene.RegionInfo.MasterAvatarLastName;
90 IDK = Convert.ToString(_idk_++);
91
92 // OpenChannel conditionally establishes a connection to the
93 // IRC server. The request will either succeed, or it will
94 // throw an exception.
95
96 ChannelState.OpenChannel(this, config);
97
98 // Connect channel to world events
99
100 scene.EventManager.OnChatFromWorld += OnSimChat;
101 scene.EventManager.OnChatFromClient += OnSimChat;
102 scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
103 scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
104
105 m_log.InfoFormat("[IRC-Region {0}] Initialization complete", Region);
106
107 }
108
109 // Auto cleanup when abandoned
110
111 ~RegionState()
112 {
113 if (cs != null)
114 cs.RemoveRegion(this);
115 }
116
117 // Called by PostInitialize after all regions have been created
118
119 public void Open()
120 {
121 cs.Open(this);
122 enabled = true;
123 }
124
125 // Called by IRCBridgeModule.Close immediately prior to unload
126 // of the module for this region. This happens when the region
127 // is being removed or the server is terminating. The IRC
128 // BridgeModule will remove the region from the region list
129 // when control returns.
130
131 public void Close()
132 {
133 enabled = false;
134 cs.Close(this);
135 }
136
137 // The agent has disconnected, cleanup associated resources
138
139 private void OnClientLoggedOut(IClientAPI client)
140 {
141 try
142 {
143 if (clients.Contains(client))
144 {
145 if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting))
146 {
147 m_log.InfoFormat("[IRC-Region {0}]: {1} has left", Region, client.Name);
148 cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has left", client.Name));
149 }
150 client.OnLogout -= OnClientLoggedOut;
151 client.OnConnectionClosed -= OnClientLoggedOut;
152 clients.Remove(client);
153 }
154 }
155 catch (Exception ex)
156 {
157 m_log.ErrorFormat("[IRC-Region {0}]: ClientLoggedOut exception: {1}", Region, ex.Message);
158 m_log.Debug(ex);
159 }
160 }
161
162 // This event indicates that the agent has left the building. We should treat that the same
163 // as if the agent has logged out (we don't want cross-region noise - or do we?)
164
165 private void OnMakeChildAgent(ScenePresence presence)
166 {
167
168 IClientAPI client = presence.ControllingClient;
169
170 try
171 {
172 if (clients.Contains(client))
173 {
174 if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting))
175 {
176 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
177 m_log.DebugFormat("[IRC-Region {0}] {1} has left", Region, clientName);
178 cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has left", clientName));
179 }
180 client.OnLogout -= OnClientLoggedOut;
181 client.OnConnectionClosed -= OnClientLoggedOut;
182 clients.Remove(client);
183 }
184 }
185 catch (Exception ex)
186 {
187 m_log.ErrorFormat("[IRC-Region {0}]: MakeChildAgent exception: {1}", Region, ex.Message);
188 m_log.Debug(ex);
189 }
190
191 }
192
193 // An agent has entered the region (from another region). Add the client to the locally
194 // known clients list
195
196 private void OnMakeRootAgent(ScenePresence presence)
197 {
198
199 IClientAPI client = presence.ControllingClient;
200
201 try
202 {
203 if (!clients.Contains(client))
204 {
205 client.OnLogout += OnClientLoggedOut;
206 client.OnConnectionClosed += OnClientLoggedOut;
207 clients.Add(client);
208 if (enabled && (cs.irc.Enabled) && (cs.irc.Connected) && (cs.ClientReporting))
209 {
210 string clientName = String.Format("{0} {1}", presence.Firstname, presence.Lastname);
211 m_log.DebugFormat("[IRC-Region {0}] {1} has arrived", Region, clientName);
212 cs.irc.PrivMsg(cs.NoticeMessageFormat, cs.irc.Nick, Region, String.Format("{0} has arrived", clientName));
213 }
214 }
215 }
216 catch (Exception ex)
217 {
218 m_log.ErrorFormat("[IRC-Region {0}]: MakeRootAgent exception: {1}", Region, ex.Message);
219 m_log.Debug(ex);
220 }
221
222 }
223
224 // This handler detects chat events int he virtual world.
225
226 public void OnSimChat(Object sender, OSChatMessage msg)
227 {
228
229 // early return if this comes from the IRC forwarder
230
231 if (cs.irc.Equals(sender)) return;
232
233 // early return if nothing to forward
234
235 if (msg.Message.Length == 0) return;
236
237 // check for commands coming from avatars or in-world
238 // object (if commands are enabled)
239
240 if (cs.CommandsEnabled && msg.Channel == cs.CommandChannel)
241 {
242
243 m_log.DebugFormat("[IRC-Region {0}] command on channel {1}: {2}", Region, msg.Channel, msg.Message);
244
245 string[] messages = msg.Message.Split(' ');
246 string command = messages[0].ToLower();
247
248 try
249 {
250 switch (command)
251 {
252
253 // These commands potentially require a change in the
254 // underlying ChannelState.
255
256 case "server":
257 cs.Close(this);
258 cs = cs.UpdateServer(this, messages[1]);
259 cs.Open(this);
260 break;
261 case "port":
262 cs.Close(this);
263 cs = cs.UpdatePort(this, messages[1]);
264 cs.Open(this);
265 break;
266 case "channel":
267 cs.Close(this);
268 cs = cs.UpdateChannel(this, messages[1]);
269 cs.Open(this);
270 break;
271 case "nick":
272 cs.Close(this);
273 cs = cs.UpdateNickname(this, messages[1]);
274 cs.Open(this);
275 break;
276
277 // These may also (but are less likely) to require a
278 // change in ChannelState.
279
280 case "client-reporting":
281 cs = cs.UpdateClientReporting(this, messages[1]);
282 break;
283 case "in-channel":
284 cs = cs.UpdateRelayIn(this, messages[1]);
285 break;
286 case "out-channel":
287 cs = cs.UpdateRelayOut(this, messages[1]);
288 break;
289
290 // These are all taken to be temporary changes in state
291 // so the underlying connector remains intact. But note
292 // that with regions sharing a connector, there could
293 // be interference.
294
295 case "close":
296 enabled = false;
297 cs.Close(this);
298 break;
299
300 case "connect":
301 enabled = true;
302 cs.Open(this);
303 break;
304
305 case "reconnect":
306 enabled = true;
307 cs.Close(this);
308 cs.Open(this);
309 break;
310
311 // This one is harmless as far as we can judge from here.
312 // If it is not, then the complaints will eventually make
313 // that evident.
314
315 default:
316 m_log.DebugFormat("[IRC-Region {0}] Forwarding unrecognized command to IRC : {1}",
317 Region, msg.Message);
318 cs.irc.Send(msg.Message);
319 break;
320 }
321 }
322 catch (Exception ex)
323 {
324 m_log.WarnFormat("[IRC-Region {0}] error processing in-world command channel input: {1}",
325 Region, ex.Message);
326 m_log.Debug(ex);
327 }
328
329 return;
330
331 }
332
333 // The command channel remains enabled, even if we have otherwise disabled the IRC
334 // interface.
335
336 if (!enabled)
337 return;
338
339 // drop messages unless they are on a valid in-world
340 // channel as configured in the ChannelState
341
342 if (!cs.ValidInWorldChannels.Contains(msg.Channel))
343 {
344 m_log.DebugFormat("[IRC-Region {0}] dropping message {1} on channel {2}", Region, msg, msg.Channel);
345 return;
346 }
347
348 ScenePresence avatar = null;
349 string fromName = msg.From;
350
351 if (msg.Sender != null)
352 {
353 avatar = scene.GetScenePresence(msg.Sender.AgentId);
354 if (avatar != null) fromName = avatar.Name;
355 }
356
357 if (!cs.irc.Connected)
358 {
359 m_log.WarnFormat("[IRC-Region {0}] IRCConnector not connected: dropping message from {1}", Region, fromName);
360 return;
361 }
362
363 m_log.DebugFormat("[IRC-Region {0}] heard on channel {1} : {2}", Region, msg.Channel, msg.Message);
364
365 if (null != avatar && cs.RelayChat && (msg.Channel == 0 || msg.Channel == DEBUG_CHANNEL))
366 {
367 string txt = msg.Message;
368 if (txt.StartsWith("/me "))
369 txt = String.Format("{0} {1}", fromName, msg.Message.Substring(4));
370
371 cs.irc.PrivMsg(cs.PrivateMessageFormat, fromName, Region, txt);
372 return;
373 }
374
375 if (null == avatar && cs.RelayPrivateChannels && null != cs.AccessPassword &&
376 msg.Channel == cs.RelayChannelOut)
377 {
378 Match m = cs.AccessPasswordRegex.Match(msg.Message);
379 if (null != m)
380 {
381 m_log.DebugFormat("[IRC] relaying message from {0}: {1}", m.Groups["avatar"].ToString(),
382 m.Groups["message"].ToString());
383 cs.irc.PrivMsg(cs.PrivateMessageFormat, m.Groups["avatar"].ToString(),
384 scene.RegionInfo.RegionName, m.Groups["message"].ToString());
385 }
386 }
387 }
388
389 // This method gives the region an opportunity to interfere with
390 // message delivery. For now we just enforce the enable/disable
391 // flag.
392
393 internal void OSChat(Object irc, OSChatMessage msg)
394 {
395 if (enabled)
396 {
397 // m_log.DebugFormat("[IRC-OSCHAT] Region {0} being sent message", region.Region);
398 msg.Scene = scene;
399 scene.EventManager.TriggerOnChatBroadcast(irc, msg);
400 }
401 }
402
403 // This supports any local message traffic that might be needed in
404 // support of command processing. At present there is none.
405
406 internal void LocalChat(string msg)
407 {
408 if (enabled)
409 {
410 OSChatMessage osm = new OSChatMessage();
411 osm.From = "IRC Agent";
412 osm.Message = msg;
413 osm.Type = ChatTypeEnum.Region;
414 osm.Position = CenterOfRegion;
415 osm.Sender = null;
416 osm.SenderUUID = OpenMetaverse.UUID.Zero; // Hmph! Still?
417 osm.Channel = 0;
418 OSChat(this, osm);
419 }
420 }
421
422 }
423
424}