Thank you, Kinoc for the ChatModule.cs updates.

ThreadPoolClientBranch
Charles Krinke 2008-02-01 22:15:40 +00:00
parent 0c0286911d
commit 8a4e8a8e31
1 changed files with 487 additions and 71 deletions

View File

@ -52,6 +52,12 @@ namespace OpenSim.Region.Environment.Modules
private IRCChatModule m_irc = null; private IRCChatModule m_irc = null;
private string m_last_new_user = null;
private string m_last_leaving_user = null;
internal object m_syncInit = new object();
internal object m_syncLogout = new object();
private Thread m_irc_connector=null;
public ChatModule() public ChatModule()
{ {
m_log = MainLog.Instance; m_log = MainLog.Instance;
@ -59,34 +65,48 @@ namespace OpenSim.Region.Environment.Modules
public void Initialise(Scene scene, IConfigSource config) public void Initialise(Scene scene, IConfigSource config)
{ {
// wrap this in a try block so that defaults will work if lock (m_syncInit)
// the config file doesn't specify otherwise.
try
{ {
m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance); // wrap this in a try block so that defaults will work if
m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance); // the config file doesn't specify otherwise.
m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance); try
} {
catch (Exception) m_whisperdistance = config.Configs["Chat"].GetInt("whisper_distance", m_whisperdistance);
{ m_saydistance = config.Configs["Chat"].GetInt("say_distance", m_saydistance);
} m_shoutdistance = config.Configs["Chat"].GetInt("shout_distance", m_shoutdistance);
}
catch (Exception)
{
}
if (!m_scenes.Contains(scene)) if (!m_scenes.Contains(scene))
{ {
m_scenes.Add(scene); m_scenes.Add(scene);
scene.EventManager.OnNewClient += NewClient; scene.EventManager.OnNewClient += NewClient;
scene.RegisterModuleInterface<ISimChat>(this); scene.RegisterModuleInterface<ISimChat>(this);
} }
// setup IRC Relay // setup IRC Relay
m_irc = new IRCChatModule(config); if (m_irc == null) { m_irc = new IRCChatModule(config); }
if (m_irc_connector == null) { m_irc_connector = new Thread(IRCConnectRun); }
}
} }
public void PostInitialise() public void PostInitialise()
{ {
if (m_irc.Enabled) if (m_irc.Enabled)
{ {
m_irc.Connect(m_scenes); try
{
//m_irc.Connect(m_scenes);
if (m_irc_connector == null) { m_irc_connector = new Thread(IRCConnectRun); }
if (!m_irc_connector.IsAlive) { m_irc_connector.Start(); }
}
catch (Exception ex)
{
}
} }
} }
@ -107,9 +127,60 @@ namespace OpenSim.Region.Environment.Modules
public void NewClient(IClientAPI client) public void NewClient(IClientAPI client)
{ {
client.OnChatFromViewer += SimChat; try
{
client.OnChatFromViewer += SimChat;
if ((m_irc.Enabled) && (m_irc.Connected))
{
string clientName = client.FirstName + " " + client.LastName;
// handles simple case. May not work for hundred connecting in per second.
// and the NewClients calles getting interleved
// but filters out multiple reports
if (clientName != m_last_new_user)
{
m_last_new_user = clientName;
string clientRegion = FindClientRegion(client.FirstName, client.LastName);
m_irc.PrivMsg(m_irc.Nick, "Sim", "notices " + clientName + " in "+clientRegion);
}
}
client.OnLogout += ClientLoggedOut;
client.OnConnectionClosed += ClientLoggedOut;
}
catch (Exception ex)
{
m_log.Error("IRC", "NewClient exception trap:" + ex.ToString());
}
} }
public void ClientLoggedOut(IClientAPI client)
{
lock (m_syncLogout)
{
try
{
if ((m_irc.Enabled) && (m_irc.Connected))
{
string clientName = client.FirstName + " " + client.LastName;
string clientRegion = FindClientRegion(client.FirstName, client.LastName);
// handles simple case. May not work for hundred connecting in per second.
// and the NewClients calles getting interleved
// but filters out multiple reports
if (clientName != m_last_leaving_user)
{
m_last_leaving_user = clientName;
m_irc.PrivMsg(m_irc.Nick, "Sim", "notices " + clientName + " left " + clientRegion);
m_log.Verbose("IRC", "IRC watcher notices " + clientName + " left " + clientRegion);
}
}
}
catch (Exception ex)
{
m_log.Error("IRC", "ClientLoggedOut exception trap:" + ex.ToString());
}
}
}
private void TrySendChatMessage(ScenePresence presence, LLVector3 fromPos, LLVector3 regionPos, private void TrySendChatMessage(ScenePresence presence, LLVector3 fromPos, LLVector3 regionPos,
LLUUID fromAgentID, string fromName, ChatTypeEnum type, string message) LLUUID fromAgentID, string fromName, ChatTypeEnum type, string message)
{ {
@ -133,6 +204,8 @@ namespace OpenSim.Region.Environment.Modules
public void SimChat(Object sender, ChatFromViewerArgs e) public void SimChat(Object sender, ChatFromViewerArgs e)
{ {
// FROM: Sim TO: IRC
ScenePresence avatar = null; ScenePresence avatar = null;
//TODO: Move ForEachScenePresence and others into IScene. //TODO: Move ForEachScenePresence and others into IScene.
@ -163,6 +236,20 @@ namespace OpenSim.Region.Environment.Modules
fromAgentID = e.Sender.AgentId; fromAgentID = e.Sender.AgentId;
} }
// Try to reconnect to server if not connected
if ((m_irc.Enabled)&&(!m_irc.Connected))
{
// In a non-blocking way. Eventually the connector will get it started
try
{
if (m_irc_connector == null) { m_irc_connector = new Thread(IRCConnectRun); }
if (!m_irc_connector.IsAlive) { m_irc_connector.Start(); }
}
catch (Exception ex)
{
}
}
if (e.Message.Length > 0) if (e.Message.Length > 0)
{ {
if (m_irc.Connected && (avatar != null)) // this is to keep objects from talking to IRC if (m_irc.Connected && (avatar != null)) // this is to keep objects from talking to IRC
@ -183,6 +270,40 @@ namespace OpenSim.Region.Environment.Modules
} }
} }
} }
// if IRC is enabled then just keep trying using a monitor thread
public void IRCConnectRun()
{
while(true)
{
if ((m_irc.Enabled)&&(!m_irc.Connected))
{
m_irc.Connect(m_scenes);
}
Thread.Sleep(15000);
}
}
public string FindClientRegion(string client_FirstName,string client_LastName)
{
string sourceRegion = null;
foreach (Scene s in m_scenes)
{
s.ForEachScenePresence(delegate(ScenePresence presence)
{
if ((presence.IsChildAgent==false)
&&(presence.Firstname==client_FirstName)
&&(presence.Lastname==client_LastName))
{
sourceRegion = presence.Scene.RegionInfo.RegionName;
//sourceRegion= s.RegionInfo.RegionName;
}
});
if (sourceRegion != null) return sourceRegion;
}
return "Sim";
}
} }
internal class IRCChatModule internal class IRCChatModule
@ -191,6 +312,7 @@ namespace OpenSim.Region.Environment.Modules
private uint m_port = 6668; private uint m_port = 6668;
private string m_user = "USER OpenSimBot 8 * :I'm a OpenSim to irc bot"; private string m_user = "USER OpenSimBot 8 * :I'm a OpenSim to irc bot";
private string m_nick = null; private string m_nick = null;
private string m_basenick = null;
private string m_channel = null; private string m_channel = null;
private NetworkStream m_stream; private NetworkStream m_stream;
@ -200,11 +322,13 @@ namespace OpenSim.Region.Environment.Modules
private Thread pingSender; private Thread pingSender;
private Thread listener; private Thread listener;
internal object m_syncConnect = new object();
private bool m_enabled = false; private bool m_enabled = false;
private bool m_connected = false; private bool m_connected = false;
private List<Scene> m_scenes = null; private List<Scene> m_scenes = null;
private List<Scene> m_last_scenes = null;
private LogBase m_log; private LogBase m_log;
public IRCChatModule(IConfigSource config) public IRCChatModule(IConfigSource config)
@ -214,15 +338,32 @@ namespace OpenSim.Region.Environment.Modules
m_writer = null; m_writer = null;
m_reader = null; m_reader = null;
// configuration in OpenSim.ini
// [IRC]
// server = chat.freenode.net
// nick = OSimBot_mysim
// ;username = USER OpenSimBot 8 * :I'm a OpenSim to irc bot
// ; username is the IRC command line sent
// ; USER <irc_user> <visible=8,invisible=0> * : <IRC_realname>
// channel = #opensim-regions
// port = 6667
//
// Traps I/O disconnects so it does not crash the sim
// Trys to reconnect if disconnected and someone says something
// Tells IRC server "QUIT" when doing a close (just to be nice)
// Default port back to 6667
try try
{ {
m_server = config.Configs["IRC"].GetString("server"); m_server = config.Configs["IRC"].GetString("server");
m_nick = config.Configs["IRC"].GetString("nick"); m_nick = config.Configs["IRC"].GetString("nick");
m_basenick = m_nick;
m_channel = config.Configs["IRC"].GetString("channel"); m_channel = config.Configs["IRC"].GetString("channel");
m_port = (uint) config.Configs["IRC"].GetInt("port", (int) m_port); m_port = (uint) config.Configs["IRC"].GetInt("port", (int) m_port);
m_user = config.Configs["IRC"].GetString("username", m_user); m_user = config.Configs["IRC"].GetString("username", m_user);
if (m_server != null && m_nick != null && m_channel != null) if (m_server != null && m_nick != null && m_channel != null)
{ {
m_nick = m_nick + Util.RandomClass.Next(1, 99);
m_enabled = true; m_enabled = true;
} }
} }
@ -235,37 +376,42 @@ namespace OpenSim.Region.Environment.Modules
public bool Connect(List<Scene> scenes) public bool Connect(List<Scene> scenes)
{ {
try lock (m_syncConnect)
{ {
m_scenes = scenes; try
{
if (m_connected) return true;
m_scenes = scenes;
if (m_last_scenes == null) { m_last_scenes = scenes; }
m_tcp = new TcpClient(m_server, (int) m_port); m_tcp = new TcpClient(m_server, (int)m_port);
m_log.Verbose("IRC", "Connecting..."); m_log.Verbose("IRC", "Connecting...");
m_stream = m_tcp.GetStream(); m_stream = m_tcp.GetStream();
m_log.Verbose("IRC", "Connected to " + m_server); m_log.Verbose("IRC", "Connected to " + m_server);
m_reader = new StreamReader(m_stream); m_reader = new StreamReader(m_stream);
m_writer = new StreamWriter(m_stream); m_writer = new StreamWriter(m_stream);
pingSender = new Thread(new ThreadStart(PingRun)); pingSender = new Thread(new ThreadStart(PingRun));
pingSender.Start(); pingSender.Start();
listener = new Thread(new ThreadStart(ListenerRun)); listener = new Thread(new ThreadStart(ListenerRun));
listener.Start(); listener.Start();
m_writer.WriteLine(m_user); m_writer.WriteLine(m_user);
m_writer.Flush(); m_writer.Flush();
m_writer.WriteLine("NICK " + m_nick); m_writer.WriteLine("NICK " + m_nick);
m_writer.Flush(); m_writer.Flush();
m_writer.WriteLine("JOIN " + m_channel); m_writer.WriteLine("JOIN " + m_channel);
m_writer.Flush(); m_writer.Flush();
m_log.Verbose("IRC", "Connection fully established"); m_log.Verbose("IRC", "Connection fully established");
m_connected = true; m_connected = true;
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
return m_connected;
} }
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
return m_connected;
} }
public bool Enabled public bool Enabled
@ -278,26 +424,53 @@ namespace OpenSim.Region.Environment.Modules
get { return m_connected; } get { return m_connected; }
} }
public string Nick
{
get { return m_nick; }
}
public void Reconnect()
{
m_connected = false;
listener.Abort();
pingSender.Abort();
m_writer.Close();
m_reader.Close();
m_tcp.Close();
if (m_enabled) { Connect(m_last_scenes); }
}
public void PrivMsg(string from, string region, string msg) public void PrivMsg(string from, string region, string msg)
{ {
// One message to the IRC server
try try
{ {
m_writer.WriteLine("PRIVMSG {0} :<{1} in {2}>: {3}", m_channel, from, region, msg); m_writer.WriteLine("PRIVMSG {0} :<{1} in {2}>: {3}", m_channel, from, region, msg);
m_writer.Flush(); m_writer.Flush();
m_log.Verbose("IRC", "PrivMsg " + from + " in " + region + " :" + msg);
} }
catch (IOException) catch (IOException)
{ {
m_log.Error("IRC", "Disconnected from IRC server."); m_log.Error("IRC", "Disconnected from IRC server.(PrivMsg)");
listener.Abort(); Reconnect();
pingSender.Abort(); }
m_connected = false; catch (Exception ex)
{
m_log.Error("IRC", "PrivMsg exception trap:" + ex.ToString());
} }
} }
private Dictionary<string, string> ExtractMsg(string input) private Dictionary<string, string> ExtractMsg(string input)
{ {
//examines IRC commands and extracts any private messages
// which will then be reboadcast in the Sim
m_log.Verbose("IRC", "ExtractMsg: " + input);
Dictionary<string, string> result = null; Dictionary<string, string> result = null;
string regex = @":(?<nick>\w*)!~(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)"; //string regex = @":(?<nick>\w*)!~(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
string regex = @":(?<nick>\w*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
Regex RE = new Regex(regex, RegexOptions.Multiline); Regex RE = new Regex(regex, RegexOptions.Multiline);
MatchCollection matches = RE.Matches(input); MatchCollection matches = RE.Matches(input);
// Get some direct matches $1 $4 is a // Get some direct matches $1 $4 is a
@ -322,11 +495,28 @@ namespace OpenSim.Region.Environment.Modules
public void PingRun() public void PingRun()
{ {
while (true) // IRC keep alive thread
// send PING ever 15 seconds
while (true)
{ {
m_writer.WriteLine("PING :" + m_server); try
m_writer.Flush(); {
Thread.Sleep(15000); if (m_connected == true)
{
m_writer.WriteLine("PING :" + m_server);
m_writer.Flush();
Thread.Sleep(15000);
}
}
catch (IOException)
{
m_log.Error("IRC", "Disconnected from IRC server.(PingRun)");
Reconnect();
}
catch (Exception ex)
{
m_log.Error("IRC", "PingRun exception trap:" + ex.ToString());
}
} }
} }
@ -336,36 +526,262 @@ namespace OpenSim.Region.Environment.Modules
LLVector3 pos = new LLVector3(128, 128, 20); LLVector3 pos = new LLVector3(128, 128, 20);
while (true) while (true)
{ {
while ((inputLine = m_reader.ReadLine()) != null) while ((m_connected == true) && ((inputLine = m_reader.ReadLine()) != null))
{ {
// Console.WriteLine(inputLine); try
if (inputLine.Contains(m_channel))
{ {
Dictionary<string, string> data = ExtractMsg(inputLine); // Console.WriteLine(inputLine);
if (data != null) if (inputLine.Contains(m_channel))
{ {
foreach (Scene m_scene in m_scenes) Dictionary<string, string> data = ExtractMsg(inputLine);
// Any chat ???
if (data != null)
{ {
m_scene.ForEachScenePresence(delegate(ScenePresence avatar) foreach (Scene m_scene in m_scenes)
{ {
if (!avatar.IsChildAgent) m_scene.ForEachScenePresence(delegate(ScenePresence avatar)
{ {
avatar.ControllingClient.SendChatMessage( if (!avatar.IsChildAgent)
Helpers.StringToField(data["msg"]), 255, {
pos, data["nick"], avatar.ControllingClient.SendChatMessage(
LLUUID.Zero); Helpers.StringToField(data["msg"]), 255,
} pos, data["nick"],
}); LLUUID.Zero);
}
});
}
} }
else
{
// Was an command from the IRC server
ProcessIRCCommand(inputLine);
}
}
else
{
// Was an command from the IRC server
ProcessIRCCommand(inputLine);
} }
Thread.Sleep(150);
}
catch (IOException)
{
m_log.Error("IRC", "ListenerRun IOException. Disconnected from IRC server ??? (ListenerRun)");
Reconnect();
}
catch (Exception ex)
{
m_log.Error("IRC", "ListenerRun exception trap:" + ex.ToString());
} }
} }
Thread.Sleep(50);
} }
} }
public void BroadcastSim(string message,string sender)
{
LLVector3 pos = new LLVector3(128, 128, 20);
try
{
foreach (Scene m_scene in m_scenes)
{
m_scene.ForEachScenePresence(delegate(ScenePresence avatar)
{
if (!avatar.IsChildAgent)
{
avatar.ControllingClient.SendChatMessage(
Helpers.StringToField(message), 255,
pos, sender,
LLUUID.Zero);
}
});
}
}
catch (Exception ex) // IRC gate should not crash Sim
{
m_log.Error("IRC", "BroadcastSim Exception Trap:" + ex.ToString());
}
}
public enum ErrorReplies
{
NotRegistered = 451, // ":You have not registered"
NicknameInUse = 433 // "<nick> :Nickname is already in use"
}
public enum Replies
{
MotdStart = 375, // ":- <server> Message of the day - "
Motd = 372, // ":- <text>"
EndOfMotd = 376 // ":End of /MOTD command"
}
public void ProcessIRCCommand(string command)
{
//m_log.Verbose("IRC", "ProcessIRCCommand:"+command);
string[] commArgs = new string[command.Split(' ').Length];
string c_server = m_server;
commArgs = command.Split(' ');
if (commArgs[0].Substring(0, 1) == ":")
{
commArgs[0] = commArgs[0].Remove(0, 1);
}
if (commArgs[1] == "002")
{
// fetch the correct servername
// ex: irc.freenode.net -> brown.freenode.net/kornbluth.freenode.net/...
// irc.bluewin.ch -> irc1.bluewin.ch/irc2.bluewin.ch
c_server = (commArgs[6].Split('['))[0];
m_server = c_server;
}
if (commArgs[0] == "ERROR")
{
m_log.Error("IRC", "IRC SERVER ERROR:" + command);
}
if (commArgs[0] == "PING")
{
string p_reply = "";
for (int i = 1; i < commArgs.Length; i++)
{
p_reply += commArgs[i] + " ";
}
m_writer.WriteLine("PONG " + p_reply);
m_writer.Flush();
}
else if (commArgs[0] == c_server)
{
// server message
try
{
Int32 commandCode = Int32.Parse(commArgs[1]);
switch (commandCode)
{
case (int)ErrorReplies.NicknameInUse:
// Gen a new name
m_nick = m_basenick + Util.RandomClass.Next(1, 99);
m_log.Error("IRC", "IRC SERVER reports NicknameInUse, trying " + m_nick);
// Retry
m_writer.WriteLine("NICK " + m_nick);
m_writer.Flush();
m_writer.WriteLine("JOIN " + m_channel);
m_writer.Flush();
break;
case (int)ErrorReplies.NotRegistered:
break;
case (int)Replies.EndOfMotd:
break;
}
}
catch (Exception ex)
{
}
}
else
{
// Normal message
string commAct = commArgs[1];
switch (commAct)
{
case "JOIN": eventIrcJoin(commArgs); break;
case "PART": eventIrcPart(commArgs); break;
case "MODE": eventIrcMode(commArgs); break;
case "NICK": eventIrcNickChange(commArgs); break;
case "KICK": eventIrcKick(commArgs); break;
case "QUIT": eventIrcQuit(commArgs); break;
case "PONG": break; // that's nice
}
}
}
public void eventIrcJoin(string[] commArgs)
{
string IrcChannel = commArgs[2];
string IrcUser = commArgs[0].Split('!')[0];
BroadcastSim(IrcUser + " is joining " + IrcChannel, m_nick);
}
public void eventIrcPart(string[] commArgs)
{
string IrcChannel = commArgs[2];
string IrcUser = commArgs[0].Split('!')[0];
BroadcastSim(IrcUser + " is parting " + IrcChannel, m_nick);
}
public void eventIrcMode(string[] commArgs)
{
string IrcChannel = commArgs[2];
string IrcUser = commArgs[0].Split('!')[0];
string UserMode = "";
for (int i = 3; i < commArgs.Length; i++)
{
UserMode += commArgs[i] + " ";
}
if (UserMode.Substring(0, 1) == ":")
{
UserMode = UserMode.Remove(0, 1);
}
}
public void eventIrcNickChange(string[] commArgs)
{
string UserOldNick = commArgs[0].Split('!')[0];
string UserNewNick = commArgs[2].Remove(0, 1);
BroadcastSim(UserOldNick + " changed their nick to " + UserNewNick, m_nick);
}
public void eventIrcKick(string[] commArgs)
{
string UserKicker = commArgs[0].Split('!')[0];
string UserKicked = commArgs[3];
string IrcChannel = commArgs[2];
string KickMessage = "";
for (int i = 4; i < commArgs.Length; i++)
{
KickMessage += commArgs[i] + " ";
}
BroadcastSim(UserKicker + " kicked " + UserKicked +" on "+IrcChannel+" saying "+KickMessage, m_nick);
if (UserKicked == m_nick)
{
BroadcastSim("Hey, that was me!!!", m_nick);
}
}
public void eventIrcQuit(string[] commArgs)
{
string IrcUser = commArgs[0].Split('!')[0];
string QuitMessage = "";
for (int i = 2; i < commArgs.Length; i++)
{
QuitMessage += commArgs[i] + " ";
}
BroadcastSim(IrcUser + " quits saying " + QuitMessage, m_nick);
}
public void Close() public void Close()
{ {
m_connected = false;
m_writer.WriteLine("QUIT :" + m_nick + " to " + m_channel + " wormhole with " + m_server + " closing");
m_writer.Flush();
listener.Abort(); listener.Abort();
pingSender.Abort(); pingSender.Abort();
m_writer.Close(); m_writer.Close();