aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Environment/Modules/Avatar
diff options
context:
space:
mode:
authorDr Scofield2008-05-26 11:56:04 +0000
committerDr Scofield2008-05-26 11:56:04 +0000
commit42cdf3c240a9cba283fe12576b889e8b46f7b3ce (patch)
treed5d67f328a4d4ec7c27a949a053f0b6d6c2c8400 /OpenSim/Region/Environment/Modules/Avatar
parent* Minor: method documentation fiddling in SceneObjectGroup (diff)
downloadopensim-SC_OLD-42cdf3c240a9cba283fe12576b889e8b46f7b3ce.zip
opensim-SC_OLD-42cdf3c240a9cba283fe12576b889e8b46f7b3ce.tar.gz
opensim-SC_OLD-42cdf3c240a9cba283fe12576b889e8b46f7b3ce.tar.bz2
opensim-SC_OLD-42cdf3c240a9cba283fe12576b889e8b46f7b3ce.tar.xz
Adding OnChatBroadcast event logic to EventManager providing
a clean interface for Sim broadcasts. Added SimBroadcast support to ChatModule. Removing all code from IRCBridgeModule dealing with agent/client directly. Cleaning up ChatModule. Polishing IRC messages, adding support for "/me" (both directions).
Diffstat (limited to 'OpenSim/Region/Environment/Modules/Avatar')
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs101
-rw-r--r--OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs156
2 files changed, 149 insertions, 108 deletions
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs
index 9730b02..e12588c 100644
--- a/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs
+++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/ChatModule.cs
@@ -65,6 +65,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
65 m_scenes.Add(scene); 65 m_scenes.Add(scene);
66 scene.EventManager.OnNewClient += NewClient; 66 scene.EventManager.OnNewClient += NewClient;
67 scene.EventManager.OnChatFromWorld += SimChat; 67 scene.EventManager.OnChatFromWorld += SimChat;
68 scene.EventManager.OnChatBroadcast += SimBroadcast;
68 } 69 }
69 70
70 // wrap this in a try block so that defaults will work if 71 // wrap this in a try block so that defaults will work if
@@ -104,8 +105,34 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
104 #endregion 105 #endregion
105 106
106 #region ISimChat Members 107 #region ISimChat Members
108 public void SimBroadcast(Object sender, ChatFromViewerArgs c)
109 {
110 // We only want to relay stuff on channel 0
111 if (c.Channel != 0 && c.Channel != DEBUG_CHANNEL) return;
112
113 if (c.Channel == DEBUG_CHANNEL)
114 c.Type = ChatTypeEnum.DebugChannel;
115
116 // chat works by redistributing every incoming chat
117 // message to each avatar in the scene
118 LLVector3 pos = new LLVector3(128, 128, 30);
119 ((Scene)c.Scene).ForEachScenePresence(delegate(ScenePresence presence)
120 {
121 if (!presence.IsChildAgent) return;
122
123 presence.ControllingClient.SendChatMessage(c.Message,
124 1, //255,
125 pos, c.From, LLUUID.Zero,
126 c.Channel == DEBUG_CHANNEL? (byte)ChatSourceType.Object : (byte)ChatSourceType.Agent,
127 (byte)ChatAudibleLevel.Fully);
128 });
129 }
130
107 public void SimChat(Object sender, ChatFromViewerArgs e) 131 public void SimChat(Object sender, ChatFromViewerArgs e)
108 { 132 {
133 // early return if not on public or debug channel
134 if (e.Channel != 0 && e.Channel != DEBUG_CHANNEL) return;
135
109 ScenePresence avatar = null; 136 ScenePresence avatar = null;
110 Scene scene = (Scene) e.Scene; 137 Scene scene = (Scene) e.Scene;
111 138
@@ -136,32 +163,28 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
136 fromAgentID = e.Sender.AgentId; 163 fromAgentID = e.Sender.AgentId;
137 } 164 }
138 165
139 // We only want to relay stuff on channel 0 166 if (e.Channel == DEBUG_CHANNEL)
140 if (e.Channel == 0 || e.Channel == DEBUG_CHANNEL) 167 e.Type = ChatTypeEnum.DebugChannel;
141 {
142 if (e.Channel == DEBUG_CHANNEL)
143 e.Type = ChatTypeEnum.DebugChannel;
144 168
145 // chat works by redistributing every incoming chat 169 // chat works by redistributing every incoming chat
146 // message to each avatar in the scene 170 // message to each avatar in the scene
147 foreach (Scene s in m_scenes) 171 foreach (Scene s in m_scenes)
148 { 172 {
149 s.ForEachScenePresence(delegate(ScenePresence presence) 173 s.ForEachScenePresence(delegate(ScenePresence presence)
174 {
175 if (e.Channel == DEBUG_CHANNEL)
150 { 176 {
151 if (e.Channel == DEBUG_CHANNEL) 177 TrySendChatMessage(presence, fromPos, regionPos,
152 { 178 fromAgentID, fromName, e.Type,
153 TrySendChatMessage(presence, fromPos, regionPos, 179 message, ChatSourceType.Object);
154 fromAgentID, fromName, e.Type, 180 }
155 message, ChatSourceType.Object); 181 else
156 } 182 {
157 else 183 TrySendChatMessage(presence, fromPos, regionPos,
158 { 184 fromAgentID, fromName, e.Type,
159 TrySendChatMessage(presence, fromPos, regionPos, 185 message, ChatSourceType.Agent);
160 fromAgentID, fromName, e.Type, 186 }
161 message, ChatSourceType.Agent); 187 });
162 }
163 });
164 }
165 } 188 }
166 } 189 }
167 190
@@ -183,23 +206,23 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
183 LLUUID fromAgentID, string fromName, ChatTypeEnum type, 206 LLUUID fromAgentID, string fromName, ChatTypeEnum type,
184 string message, ChatSourceType src) 207 string message, ChatSourceType src)
185 { 208 {
186 if (!presence.IsChildAgent) 209 // don't send stuff to child agents
210 if (presence.IsChildAgent) return;
211
212 LLVector3 fromRegionPos = fromPos + regionPos;
213 LLVector3 toRegionPos = presence.AbsolutePosition + regionPos;
214 int dis = Math.Abs((int) Util.GetDistanceTo(toRegionPos, fromRegionPos));
215
216 if (type == ChatTypeEnum.Whisper && dis > m_whisperdistance ||
217 type == ChatTypeEnum.Say && dis > m_saydistance ||
218 type == ChatTypeEnum.Shout && dis > m_shoutdistance)
187 { 219 {
188 LLVector3 fromRegionPos = fromPos + regionPos; 220 return;
189 LLVector3 toRegionPos = presence.AbsolutePosition + regionPos;
190 int dis = Math.Abs((int) Util.GetDistanceTo(toRegionPos, fromRegionPos));
191
192 if (type == ChatTypeEnum.Whisper && dis > m_whisperdistance ||
193 type == ChatTypeEnum.Say && dis > m_saydistance ||
194 type == ChatTypeEnum.Shout && dis > m_shoutdistance)
195 {
196 return;
197 }
198
199 // TODO: should change so the message is sent through the avatar rather than direct to the ClientView
200 presence.ControllingClient.SendChatMessage(message, (byte) type, fromPos, fromName,
201 fromAgentID,(byte)src,(byte)ChatAudibleLevel.Fully);
202 } 221 }
222
223 // TODO: should change so the message is sent through the avatar rather than direct to the ClientView
224 presence.ControllingClient.SendChatMessage(message, (byte) type, fromPos, fromName,
225 fromAgentID,(byte)src,(byte)ChatAudibleLevel.Fully);
203 } 226 }
204 } 227 }
205} \ No newline at end of file 228} \ No newline at end of file
diff --git a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
index 283551b..18106be 100644
--- a/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
+++ b/OpenSim/Region/Environment/Modules/Avatar/Chat/IRCBridgeModule.cs
@@ -43,7 +43,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
43{ 43{
44 public class IRCBridgeModule : IRegionModule, ISimChat 44 public class IRCBridgeModule : IRegionModule, ISimChat
45 { 45 {
46 private static readonly ILog m_log = 46 private static readonly ILog m_log =
47 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 47 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
48 48
49 private const int DEBUG_CHANNEL = 2147483647; 49 private const int DEBUG_CHANNEL = 2147483647;
@@ -94,34 +94,34 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
94 m_irc_connector.Name = "IRCConnectorThread"; 94 m_irc_connector.Name = "IRCConnectorThread";
95 m_irc_connector.IsBackground = true; 95 m_irc_connector.IsBackground = true;
96 } 96 }
97 m_log.InfoFormat("[IRC] initialized for {0}, nick: {1} ", scene.RegionInfo.RegionName, 97 m_log.InfoFormat("[IRC] initialized for {0}, nick: {1} ", scene.RegionInfo.RegionName,
98 m_defaultzone); 98 m_defaultzone);
99 } 99 }
100 } 100 }
101 101
102 public void PostInitialise() 102 public void PostInitialise()
103 { 103 {
104 if (m_irc.Enabled) 104 if (!m_irc.Enabled) return;
105
106 try
105 { 107 {
106 try 108 //m_irc.Connect(m_scenes);
109 if (m_irc_connector == null)
107 { 110 {
108 //m_irc.Connect(m_scenes); 111 m_irc_connector = new Thread(IRCConnectRun);
109 if (m_irc_connector == null) 112 m_irc_connector.Name = "IRCConnectorThread";
110 { 113 m_irc_connector.IsBackground = true;
111 m_irc_connector = new Thread(IRCConnectRun);
112 m_irc_connector.Name = "IRCConnectorThread";
113 m_irc_connector.IsBackground = true;
114 }
115 if (!m_irc_connector.IsAlive)
116 {
117 m_irc_connector.Start();
118 ThreadTracker.Add(m_irc_connector);
119 }
120 } 114 }
121 catch (Exception) 115
116 if (!m_irc_connector.IsAlive)
122 { 117 {
118 m_irc_connector.Start();
119 ThreadTracker.Add(m_irc_connector);
123 } 120 }
124 } 121 }
122 catch (Exception)
123 {
124 }
125 } 125 }
126 126
127 public void Close() 127 public void Close()
@@ -145,6 +145,13 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
145 145
146 public void SimChat(Object sender, ChatFromViewerArgs e) 146 public void SimChat(Object sender, ChatFromViewerArgs e)
147 { 147 {
148 // We only want to relay stuff on channel 0
149 if (e.Channel != 0) return;
150 if (e.Message.Length == 0) return;
151
152 // not interested in our own babblings
153 if (m_irc.Equals(sender)) return;
154
148 ScenePresence avatar = null; 155 ScenePresence avatar = null;
149 Scene scene = (Scene) e.Scene; 156 Scene scene = (Scene) e.Scene;
150 157
@@ -187,21 +194,21 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
187 } 194 }
188 } 195 }
189 196
190 197 if (e.Message.StartsWith("/me ") && (null != avatar))
191 // We only want to relay stuff on channel 0 198 e.Message = String.Format("{0} {1}", fromName, e.Message.Substring(4));
192 if (e.Channel == 0 || e.Channel == DEBUG_CHANNEL) 199 if (e.Channel == 0 || e.Channel == DEBUG_CHANNEL)
193 { 200 {
194 if (e.Channel == DEBUG_CHANNEL) 201 if (e.Channel == DEBUG_CHANNEL)
195 e.Type = ChatTypeEnum.DebugChannel; 202 e.Type = ChatTypeEnum.DebugChannel;
196 203
197 // IRC stuff 204 // IRC stuff
198 if (e.Message.Length > 0 && e.Channel == 0) 205 if (e.Message.Length > 0 && e.Channel == 0)
199 { 206 {
200 if (m_irc.Connected && (avatar != null)) // this is to keep objects from talking to IRC 207 if (m_irc.Connected && (avatar != null)) // this is to keep objects from talking to IRC
201 { 208 {
202 m_irc.PrivMsg(fromName, scene.RegionInfo.RegionName, e.Message); 209 m_irc.PrivMsg(fromName, scene.RegionInfo.RegionName, e.Message);
203 } 210 }
204 } 211 }
205 } 212 }
206 } 213 }
207 214
@@ -222,7 +229,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
222 if ((m_irc.Enabled) && (m_irc.Connected)) 229 if ((m_irc.Enabled) && (m_irc.Connected))
223 { 230 {
224 m_log.DebugFormat("[IRC] {0} logging on", clientName); 231 m_log.DebugFormat("[IRC] {0} logging on", clientName);
225 m_irc.PrivMsg(m_irc.Nick, "Sim", 232 m_irc.PrivMsg(m_irc.Nick, "Sim",
226 String.Format("notices {0} logging on", clientName)); 233 String.Format("notices {0} logging on", clientName));
227 } 234 }
228 m_last_new_user = clientName; 235 m_last_new_user = clientName;
@@ -246,7 +253,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
246 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} in {1}", clientName, regionName)); 253 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} in {1}", clientName, regionName));
247 } 254 }
248 } 255 }
249 catch (Exception ex) 256 catch (Exception)
250 { 257 {
251 } 258 }
252 } 259 }
@@ -263,7 +270,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
263 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} left {1}", clientName, regionName)); 270 m_irc.PrivMsg(m_irc.Nick, "Sim", String.Format("notices {0} left {1}", clientName, regionName));
264 } 271 }
265 } 272 }
266 catch (Exception ex) 273 catch (Exception)
267 { 274 {
268 } 275 }
269 } 276 }
@@ -288,7 +295,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
288 m_log.InfoFormat("[IRC]: {0} logging out", clientName); 295 m_log.InfoFormat("[IRC]: {0} logging out", clientName);
289 } 296 }
290 297
291 if (m_last_new_user == clientName) 298 if (m_last_new_user == clientName)
292 m_last_new_user = null; 299 m_last_new_user = null;
293 } 300 }
294 } 301 }
@@ -430,6 +437,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
430 try 437 try
431 { 438 {
432 if (m_connected) return true; 439 if (m_connected) return true;
440
433 m_scenes = scenes; 441 m_scenes = scenes;
434 if (m_last_scenes == null) 442 if (m_last_scenes == null)
435 { 443 {
@@ -517,8 +525,9 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
517 string regex = @":(?<nick>\w*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)"; 525 string regex = @":(?<nick>\w*)!(?<user>\S*) PRIVMSG (?<channel>\S+) :(?<msg>.*)";
518 Regex RE = new Regex(regex, RegexOptions.Multiline); 526 Regex RE = new Regex(regex, RegexOptions.Multiline);
519 MatchCollection matches = RE.Matches(input); 527 MatchCollection matches = RE.Matches(input);
528
520 // Get some direct matches $1 $4 is a 529 // Get some direct matches $1 $4 is a
521 if ((matches.Count == 1) && (matches[0].Groups.Count == 5)) 530 if ((matches.Count == 0) || (matches.Count != 1) || (matches[0].Groups.Count != 5))
522 { 531 {
523 result = new Dictionary<string, string>(); 532 result = new Dictionary<string, string>();
524 result.Add("nick", matches[0].Groups[1].Value); 533 result.Add("nick", matches[0].Groups[1].Value);
@@ -533,7 +542,15 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
533 { 542 {
534 m_log.Info("[IRC]: Number of groups: " + matches[0].Groups.Count); 543 m_log.Info("[IRC]: Number of groups: " + matches[0].Groups.Count);
535 } 544 }
545 return null;
536 } 546 }
547
548 result = new Dictionary<string, string>();
549 result.Add("nick", matches[0].Groups[1].Value);
550 result.Add("user", matches[0].Groups[2].Value);
551 result.Add("channel", matches[0].Groups[3].Value);
552 result.Add("msg", matches[0].Groups[4].Value);
553
537 return result; 554 return result;
538 } 555 }
539 556
@@ -581,32 +598,32 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
581 // Any chat ??? 598 // Any chat ???
582 if (data != null) 599 if (data != null)
583 { 600 {
584 foreach (Scene m_scene in m_scenes) 601 ChatFromViewerArgs c = new ChatFromViewerArgs();
602 c.Message = data["msg"];
603 c.Type = ChatTypeEnum.Say;
604 c.Channel = 0;
605 c.Position = pos;
606 c.From = data["nick"];
607 c.Sender = null;
608 c.SenderUUID = LLUUID.Zero;
609
610 // is message "\001ACTION foo
611 // bar\001"? -> "/me foo bar"
612 if ((1 == c.Message[0]) && c.Message.Substring(1).StartsWith("ACTION"))
613 c.Message = String.Format("/me {0}", c.Message.Substring(8, c.Message.Length - 9));
614
615 foreach (Scene scene in m_scenes)
585 { 616 {
586 m_scene.ForEachScenePresence(delegate(ScenePresence avatar) 617 c.Scene = scene;
587 { 618 scene.EventManager.TriggerOnChatBroadcast(this, c);
588 if (!avatar.IsChildAgent)
589 {
590 avatar.ControllingClient.SendChatMessage(
591 Helpers.StringToField(data["msg"]),
592 1, // 255,
593 pos, data["nick"],
594 LLUUID.Zero,(byte)ChatSourceType.Agent,(byte)ChatAudibleLevel.Fully);
595 }
596 });
597 } 619 }
598 } 620 }
599 else 621
600 { 622 Thread.Sleep(150);
601 // Was an command from the IRC server 623 continue;
602 ProcessIRCCommand(inputLine);
603 }
604 }
605 else
606 {
607 // Was an command from the IRC server
608 ProcessIRCCommand(inputLine);
609 } 624 }
625
626 ProcessIRCCommand(inputLine);
610 Thread.Sleep(150); 627 Thread.Sleep(150);
611 } 628 }
612 } 629 }
@@ -627,20 +644,19 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
627 LLVector3 pos = new LLVector3(128, 128, 20); 644 LLVector3 pos = new LLVector3(128, 128, 20);
628 try 645 try
629 { 646 {
647 ChatFromViewerArgs c = new ChatFromViewerArgs();
648 c.From = sender;
649 c.Message = String.Format(format, args);
650 c.Type = ChatTypeEnum.Say;
651 c.Channel = 0;
652 c.Position = new LLVector3(128, 128, 20);
653 c.Sender = null;
654 c.SenderUUID = LLUUID.Zero;
655
630 foreach (Scene m_scene in m_scenes) 656 foreach (Scene m_scene in m_scenes)
631 { 657 {
632 m_scene.ForEachScenePresence(delegate(ScenePresence avatar) 658 c.Scene = m_scene;
633 { 659 m_scene.EventManager.TriggerOnChatBroadcast(this, c);
634 if (!avatar.IsChildAgent)
635 {
636 avatar.ControllingClient.SendChatMessage(
637 Helpers.StringToField(String.Format(format, args)),
638 1, //255,
639 pos, sender, LLUUID.Zero,
640 (byte)ChatSourceType.Object,
641 (byte)ChatAudibleLevel.Fully);
642 }
643 });
644 } 660 }
645 } 661 }
646 catch (Exception ex) // IRC gate should not crash Sim 662 catch (Exception ex) // IRC gate should not crash Sim
@@ -750,15 +766,17 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
750 public void eventIrcJoin(string[] commArgs) 766 public void eventIrcJoin(string[] commArgs)
751 { 767 {
752 string IrcChannel = commArgs[2]; 768 string IrcChannel = commArgs[2];
769 if (IrcChannel.StartsWith(":"))
770 IrcChannel = IrcChannel.Substring(1);
753 string IrcUser = commArgs[0].Split('!')[0]; 771 string IrcUser = commArgs[0].Split('!')[0];
754 BroadcastSim(m_nick, "{0} is joining {1}", IrcUser, IrcChannel); 772 BroadcastSim(IrcUser, "/me joins {0}", IrcChannel);
755 } 773 }
756 774
757 public void eventIrcPart(string[] commArgs) 775 public void eventIrcPart(string[] commArgs)
758 { 776 {
759 string IrcChannel = commArgs[2]; 777 string IrcChannel = commArgs[2];
760 string IrcUser = commArgs[0].Split('!')[0]; 778 string IrcUser = commArgs[0].Split('!')[0];
761 BroadcastSim(m_nick, "{0} is parting {1}", IrcUser, IrcChannel); 779 BroadcastSim(IrcUser, "/me parts {0}", IrcChannel);
762 } 780 }
763 781
764 public void eventIrcMode(string[] commArgs) 782 public void eventIrcMode(string[] commArgs)
@@ -781,7 +799,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
781 { 799 {
782 string UserOldNick = commArgs[0].Split('!')[0]; 800 string UserOldNick = commArgs[0].Split('!')[0];
783 string UserNewNick = commArgs[2].Remove(0, 1); 801 string UserNewNick = commArgs[2].Remove(0, 1);
784 BroadcastSim(m_nick, "{0} changed their nick to {1}", UserOldNick, UserNewNick); 802 BroadcastSim(UserOldNick, "/me is now known as {0}", UserNewNick);
785 } 803 }
786 804
787 public void eventIrcKick(string[] commArgs) 805 public void eventIrcKick(string[] commArgs)
@@ -794,7 +812,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
794 { 812 {
795 KickMessage += commArgs[i] + " "; 813 KickMessage += commArgs[i] + " ";
796 } 814 }
797 BroadcastSim(m_nick, "{0} kicked {1} on {2} saying {3}", UserKicker, UserKicked, IrcChannel, KickMessage); 815 BroadcastSim(UserKicker, "/me kicks kicks {0} off {1} saying \"{2}\"", UserKicked, IrcChannel, KickMessage);
798 if (UserKicked == m_nick) 816 if (UserKicked == m_nick)
799 { 817 {
800 BroadcastSim(m_nick, "Hey, that was me!!!"); 818 BroadcastSim(m_nick, "Hey, that was me!!!");
@@ -810,7 +828,7 @@ namespace OpenSim.Region.Environment.Modules.Avatar.Chat
810 { 828 {
811 QuitMessage += commArgs[i] + " "; 829 QuitMessage += commArgs[i] + " ";
812 } 830 }
813 BroadcastSim(m_nick, "{0} quits saying {1}", IrcUser, QuitMessage); 831 BroadcastSim(IrcUser, "/me quits saying \"{0}\"", QuitMessage);
814 } 832 }
815 833
816 public void Close() 834 public void Close()