29 using System.Collections;
30 using System.Collections.Generic;
33 using System.Net.Sockets;
34 using System.Reflection;
36 using System.Text.RegularExpressions;
37 using System.Threading;
43 using OpenSim.Framework;
44 using OpenSim.Framework.Servers;
45 using OpenSim.Region.Framework.Interfaces;
46 using OpenSim.Region.Framework.Scenes;
47 using OpenSim.Region.CoreModules.Avatar.Chat;
51 [Extension(Path =
"/OpenSim/RegionModules", NodeName =
"RegionModule", Id =
"ConciergeModule")]
54 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
56 private const int DEBUG_CHANNEL = 2147483647;
58 private List<IScene> m_scenes =
new List<IScene>();
59 private List<IScene> m_conciergedScenes =
new List<IScene>();
61 private bool m_replacingChatModule =
false;
63 private IConfig m_config;
65 private string m_whoami =
"conferencier";
66 private Regex m_regions = null;
67 private string m_welcomes = null;
68 private int m_conciergeChannel = 42;
69 private string m_announceEntering =
"{0} enters {1} (now {2} visitors in this region)";
70 private string m_announceLeaving =
"{0} leaves {1} (back to {2} visitors in this region)";
71 private string m_xmlRpcPassword = String.Empty;
72 private string m_brokerURI = String.Empty;
73 private int m_brokerUpdateTimeout = 300;
75 internal object m_syncy =
new object();
77 internal bool m_enabled =
false;
79 #region ISharedRegionModule Members
82 m_config = config.Configs[
"Concierge"];
87 if (!m_config.GetBoolean(
"enabled",
false))
97 if (config.Configs[
"Chat"] == null)
102 m_replacingChatModule =
false;
106 m_replacingChatModule = !config.Configs[
"Chat"].GetBoolean(
"enabled",
true);
111 m_replacingChatModule =
false;
114 m_log.InfoFormat(
"[Concierge] {0} ChatModule", m_replacingChatModule ?
"replacing" :
"not replacing");
117 m_conciergeChannel = config.Configs[
"Concierge"].GetInt(
"concierge_channel", m_conciergeChannel);
118 m_whoami = m_config.GetString(
"whoami",
"conferencier");
119 m_welcomes = m_config.GetString(
"welcomes", m_welcomes);
120 m_announceEntering = m_config.GetString(
"announce_entering", m_announceEntering);
121 m_announceLeaving = m_config.GetString(
"announce_leaving", m_announceLeaving);
122 m_xmlRpcPassword = m_config.GetString(
"password", m_xmlRpcPassword);
123 m_brokerURI = m_config.GetString(
"broker", m_brokerURI);
124 m_brokerUpdateTimeout = m_config.GetInt(
"broker_timeout", m_brokerUpdateTimeout);
126 m_log.InfoFormat(
"[Concierge] reporting as \"{0}\" to our users", m_whoami);
129 if (m_regions == null)
131 string regions = m_config.GetString(
"regions", String.Empty);
132 if (!
String.IsNullOrEmpty(regions))
134 m_regions =
new Regex(@regions, RegexOptions.Compiled | RegexOptions.IgnoreCase);
142 if (!m_enabled)
return;
144 MainServer.Instance.AddXmlRPCHandler(
"concierge_update_welcome", XmlRpcUpdateWelcomeMethod,
false);
148 if (!m_scenes.Contains(scene))
153 m_conciergedScenes.Add(scene);
156 scene.EventManager.OnNewClient += OnNewClient;
159 scene.EventManager.OnChatFromWorld += OnChatFromWorld;
160 if (!m_replacingChatModule)
161 scene.EventManager.OnChatFromClient += OnChatFromClient;
162 scene.EventManager.OnChatBroadcast += OnChatBroadcast;
165 scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
166 scene.EventManager.OnMakeChildAgent += OnMakeChildAgent;
169 m_log.InfoFormat(
"[Concierge]: initialized for {0}", scene.RegionInfo.RegionName);
174 if (!m_enabled)
return;
176 MainServer.Instance.RemoveXmlRPCHandler(
"concierge_update_welcome");
181 scene.EventManager.OnNewClient -= OnNewClient;
184 scene.EventManager.OnChatFromWorld -= OnChatFromWorld;
185 if (!m_replacingChatModule)
186 scene.EventManager.OnChatFromClient -= OnChatFromClient;
187 scene.EventManager.OnChatBroadcast -= OnChatBroadcast;
190 scene.EventManager.OnMakeRootAgent -= OnMakeRootAgent;
191 scene.EventManager.OnMakeChildAgent -= OnMakeChildAgent;
193 if (m_scenes.Contains(scene))
195 m_scenes.Remove(scene);
198 if (m_conciergedScenes.Contains(scene))
200 m_conciergedScenes.Remove(scene);
203 m_log.InfoFormat(
"[Concierge]: removed {0}", scene.RegionInfo.RegionName);
214 new public Type ReplaceableInterface
219 public override string Name
221 get {
return "ConciergeModule"; }
225 #region ISimChat Members
228 if (m_replacingChatModule)
232 base.OnChatBroadcast(sender, c);
241 if (m_replacingChatModule)
245 c = FixPositionOfChatMessage(c);
250 if (m_conciergedScenes.Contains(c.
Scene))
260 base.OnChatBroadcast(sender, c);
266 base.OnChatFromClient(sender, c);
275 if (m_replacingChatModule)
277 if (m_conciergedScenes.Contains(c.
Scene))
287 base.OnChatBroadcast(sender, c);
292 base.OnChatFromWorld(sender, c);
301 client.OnLogout += OnClientLoggedOut;
303 if (m_replacingChatModule)
304 client.OnChatFromClient += OnChatFromClient;
311 client.OnLogout -= OnClientLoggedOut;
312 client.OnConnectionClosed -= OnClientLoggedOut;
314 if (m_conciergedScenes.Contains(client.
Scene))
317 m_log.DebugFormat(
"[Concierge]: {0} logs off from {1}", client.Name, scene.RegionInfo.RegionName);
326 if (m_conciergedScenes.Contains(agent.
Scene))
328 Scene scene = agent.Scene;
329 m_log.DebugFormat(
"[Concierge]: {0} enters {1}", agent.Name, scene.RegionInfo.RegionName);
330 WelcomeAvatar(agent, scene);
331 AnnounceToAgentsRegion(scene,
String.Format(m_announceEntering, agent.
Name,
340 if (m_conciergedScenes.Contains(agent.
Scene))
342 Scene scene = agent.Scene;
343 m_log.DebugFormat(
"[Concierge]: {0} leaves {1}", agent.Name, scene.RegionInfo.RegionName);
344 AnnounceToAgentsRegion(scene,
String.Format(m_announceLeaving, agent.
Name,
350 internal class BrokerState
353 public string Payload;
354 public HttpWebRequest Poster;
357 public BrokerState(
string uri,
string payload, HttpWebRequest poster)
367 if (
String.IsNullOrEmpty(m_brokerURI))
370 string uri = String.Format(m_brokerURI, scene.RegionInfo.RegionName, scene.RegionInfo.RegionID);
373 StringBuilder list =
new StringBuilder();
374 list.Append(String.Format(
"<avatars count=\"{0}\" region_name=\"{1}\" region_uuid=\"{2}\" timestamp=\"{3}\">\n",
377 DateTime.UtcNow.ToString(
"s")));
381 list.Append(String.Format(
" <avatar name=\"{0}\" uuid=\"{1}\" />\n", sp.Name, sp.UUID));
384 list.Append(
"</avatars>");
385 string payload = list.ToString();
388 HttpWebRequest updatePost = WebRequest.Create(uri) as HttpWebRequest;
389 updatePost.Method =
"POST";
390 updatePost.ContentType =
"text/xml";
391 updatePost.ContentLength = payload.Length;
392 updatePost.UserAgent =
"OpenSim.Concierge";
395 BrokerState bs =
new BrokerState(uri, payload, updatePost);
396 bs.Timer =
new Timer(delegate(
object state)
398 BrokerState b = state as BrokerState;
401 m_log.Debug(
"[Concierge]: async broker POST abort due to timeout");
402 }, bs, m_brokerUpdateTimeout * 1000, Timeout.Infinite);
406 updatePost.BeginGetRequestStream(UpdateBrokerSend, bs);
407 m_log.DebugFormat(
"[Concierge] async broker POST to {0} started", uri);
409 catch (WebException we)
411 m_log.ErrorFormat(
"[Concierge] async broker POST to {0} failed: {1}", uri, we.Status);
415 private void UpdateBrokerSend(IAsyncResult result)
417 BrokerState bs = null;
420 bs = result.AsyncState as BrokerState;
421 string payload = bs.Payload;
422 HttpWebRequest updatePost = bs.Poster;
424 using (StreamWriter payloadStream =
new StreamWriter(updatePost.EndGetRequestStream(result)))
426 payloadStream.Write(payload);
427 payloadStream.Close();
429 updatePost.BeginGetResponse(UpdateBrokerDone, bs);
431 catch (WebException we)
433 m_log.DebugFormat(
"[Concierge]: async broker POST to {0} failed: {1}", bs.Uri, we.Status);
437 m_log.DebugFormat(
"[Concierge]: async broker POST to {0} failed", bs.Uri);
441 private void UpdateBrokerDone(IAsyncResult result)
443 BrokerState bs = null;
446 bs = result.AsyncState as BrokerState;
447 HttpWebRequest updatePost = bs.Poster;
448 using (HttpWebResponse response = updatePost.EndGetResponse(result) as HttpWebResponse)
450 m_log.DebugFormat(
"[Concierge] broker update: status {0}", response.StatusCode);
454 catch (WebException we)
456 m_log.ErrorFormat(
"[Concierge] broker update to {0} failed with status {1}", bs.Uri, we.Status);
457 if (null != we.Response)
459 using (HttpWebResponse resp = we.Response as HttpWebResponse)
461 m_log.ErrorFormat(
"[Concierge] response from {0} status code: {1}", bs.Uri, resp.StatusCode);
462 m_log.ErrorFormat(
"[Concierge] response from {0} status desc: {1}", bs.Uri, resp.StatusDescription);
463 m_log.ErrorFormat(
"[Concierge] response from {0} server: {1}", bs.Uri, resp.Server);
465 if (resp.ContentLength > 0)
467 StreamReader content =
new StreamReader(resp.GetResponseStream());
468 m_log.ErrorFormat(
"[Concierge] response from {0} content: {1}", bs.Uri, content.ReadToEnd());
481 if (!
String.IsNullOrEmpty(m_welcomes))
483 string[] welcomes =
new string[] {
484 Path.Combine(m_welcomes, agent.Scene.RegionInfo.RegionName),
485 Path.Combine(m_welcomes,
"DEFAULT")};
486 foreach (
string welcome
in welcomes)
488 if (
File.Exists(welcome))
492 string[] welcomeLines = File.ReadAllLines(welcome);
493 foreach (
string l
in welcomeLines)
498 catch (IOException ioe)
500 m_log.ErrorFormat(
"[Concierge]: run into trouble reading welcome file {0} for region {1} for avatar {2}: {3}",
501 welcome, scene.RegionInfo.RegionName, agent.Name, ioe);
503 catch (FormatException fe)
505 m_log.ErrorFormat(
"[Concierge]: welcome file {0} is malformed: {1}", welcome, fe);
510 m_log.DebugFormat(
"[Concierge]: no welcome message for region {0}", scene.RegionInfo.RegionName);
514 static private Vector3 PosOfGod =
new Vector3(128, 128, 9999);
529 c.Type = ChatTypeEnum.Say;
531 c.Position = PosOfGod;
534 c.SenderUUID = UUID.Zero;
545 c.Type = ChatTypeEnum.Say;
547 c.Position = PosOfGod;
550 c.SenderUUID = UUID.Zero;
551 c.Scene = agent.Scene;
553 agent.ControllingClient.SendChatMessage(
555 (byte)ChatSourceType.Object, (byte)ChatAudibleLevel.Fully);
558 private static void checkStringParameters(XmlRpcRequest request,
string[] param)
560 Hashtable requestData = (Hashtable) request.Params[0];
561 foreach (
string p in param)
563 if (!requestData.Contains(p))
564 throw new Exception(
String.Format(
"missing string parameter {0}", p));
565 if (
String.IsNullOrEmpty((
string)requestData[p]))
566 throw new Exception(
String.Format(
"parameter {0} is empty", p));
572 m_log.Info(
"[Concierge]: processing UpdateWelcome request");
573 XmlRpcResponse response =
new XmlRpcResponse();
574 Hashtable responseData =
new Hashtable();
578 Hashtable requestData = (Hashtable)request.Params[0];
579 checkStringParameters(request,
new string[] {
"password",
"region",
"welcome" });
582 if (!
String.IsNullOrEmpty(m_xmlRpcPassword) &&
583 (string)requestData[
"password"] != m_xmlRpcPassword)
throw new Exception(
"wrong password");
585 if (
String.IsNullOrEmpty(m_welcomes))
586 throw new Exception(
"welcome templates are not enabled, ask your OpenSim operator to set the \"welcomes\" option in the [Concierge] section of OpenSim.ini");
588 string msg = (string)requestData[
"welcome"];
589 if (
String.IsNullOrEmpty(msg))
590 throw new Exception(
"empty parameter \"welcome\"");
592 string regionName = (string)requestData[
"region"];
593 IScene scene = m_scenes.Find(delegate(
IScene s) {
return s.RegionInfo.RegionName == regionName; });
595 throw new Exception(
String.Format(
"unknown region \"{0}\"", regionName));
597 if (!m_conciergedScenes.Contains(scene))
598 throw new Exception(
String.Format(
"region \"{0}\" is not a concierged region.", regionName));
600 string welcome = Path.Combine(m_welcomes, regionName);
601 if (
File.Exists(welcome))
603 m_log.InfoFormat(
"[Concierge]: UpdateWelcome: updating existing template \"{0}\"", welcome);
604 string welcomeBackup = String.Format(
"{0}~", welcome);
605 if (
File.Exists(welcomeBackup))
606 File.Delete(welcomeBackup);
607 File.Move(welcome, welcomeBackup);
609 File.WriteAllText(welcome, msg);
611 responseData[
"success"] =
"true";
612 response.Value = responseData;
616 m_log.InfoFormat(
"[Concierge]: UpdateWelcome failed: {0}", e.Message);
618 responseData[
"success"] =
"false";
619 responseData[
"error"] = e.Message;
621 response.Value = responseData;
623 m_log.Debug(
"[Concierge]: done processing UpdateWelcome request");
override void Close()
This is the inverse to Initialise. After a Close(), this instance won't be usable anymore...
void OnMakeChildAgent(ScenePresence agent)
Scene Scene
The scene to which this entity belongs
EventManager EventManager
string Name
Returns the full name of the agent/avatar represented by this client
void WelcomeAvatar(ScenePresence agent, Scene scene)
override void AddRegion(Scene scene)
This is called whenever a Scene is added. For shared modules, this can happen several times...
override void Initialise(IConfigSource config)
This is called to initialize the region module. For shared modules, this is called exactly once...
System.Timers.Timer Timer
override void OnChatFromClient(Object sender, OSChatMessage c)
override void OnChatBroadcast(Object sender, OSChatMessage c)
override void OnChatFromWorld(Object sender, OSChatMessage c)
override void PostInitialise()
This is called exactly once after all the shared region-modules have been instanciated and IRegionMod...
void OnClientLoggedOut(IClientAPI client)
void OnMakeRootAgent(ScenePresence agent)
void TriggerOnChatBroadcast(Object sender, OSChatMessage chat)
ChatTypeEnum Type
The type of message, eg say, shout, broadcast.
XmlRpcResponse XmlRpcUpdateWelcomeMethod(XmlRpcRequest request, IPEndPoint remoteClient)
void AnnounceToAgentsRegion(IScene scene, string msg)
override void RemoveRegion(Scene scene)
This is called whenever a Scene is removed. For shared modules, this can happen several times...
Interactive OpenSim region server
A class for triggering remote scene events.
virtual string Name
The name of this entity
void TriggerOnChatFromClient(Object sender, OSChatMessage chat)
virtual RegionInfo RegionInfo
override void OnNewClient(IClientAPI client)
void AnnounceToAgent(ScenePresence agent, string msg)
void UpdateBroker(Scene scene)