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