OSDN Git Service

Added Handler for hidden exceptions to Log into common logger
[radegast/radegast.git] / Radegast / Core / RadegastInstance.cs
1 // \r
2 // Radegast Metaverse Client\r
3 // Copyright (c) 2009, Radegast Development Team\r
4 // All rights reserved.\r
5 // \r
6 // Redistribution and use in source and binary forms, with or without\r
7 // modification, are permitted provided that the following conditions are met:\r
8 // \r
9 //     * Redistributions of source code must retain the above copyright notice,\r
10 //       this list of conditions and the following disclaimer.\r
11 //     * Redistributions in binary form must reproduce the above copyright\r
12 //       notice, this list of conditions and the following disclaimer in the\r
13 //       documentation and/or other materials provided with the distribution.\r
14 //     * Neither the name of the application "Radegast", nor the names of its\r
15 //       contributors may be used to endorse or promote products derived from\r
16 //       this software without specific prior written permission.\r
17 // \r
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"\r
19 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r
20 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\r
21 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\r
22 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\r
23 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\r
24 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\r
25 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\r
26 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\r
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
28 //\r
29 // $Id$\r
30 //\r
31 using System;\r
32 using System.Collections.Generic;\r
33 using System.IO;\r
34 using System.Reflection;\r
35 using System.Threading;\r
36 using System.Windows.Forms;\r
37 using Radegast.Commands;\r
38 using Radegast.Netcom;\r
39 using Radegast.Media;\r
40 using OpenMetaverse;\r
41 \r
42 namespace Radegast\r
43 {\r
44     public class RadegastInstance\r
45     {\r
46         private GridClient client;\r
47         private RadegastNetcom netcom;\r
48 \r
49         private StateManager state;\r
50 \r
51         private frmMain mainForm;\r
52         private TabsConsole tabsConsole;\r
53 \r
54         // Singleton, there can be only one instance\r
55         private static RadegastInstance globalInstance = null;\r
56         public static RadegastInstance GlobalInstance\r
57         {\r
58             get\r
59             {\r
60                 if (globalInstance == null)\r
61                 {\r
62                     globalInstance = new RadegastInstance(new GridClient());\r
63                 }\r
64                 return globalInstance;\r
65             }\r
66         }\r
67 \r
68         private string userDir;\r
69         /// <summary>\r
70         /// System (not grid!) user's dir\r
71         /// </summary>\r
72         public string UserDir { get { return userDir; } }\r
73 \r
74         /// <summary>\r
75         /// Grid client's user dir for settings and logs\r
76         /// </summary>\r
77         public string ClientDir\r
78         {\r
79             get\r
80             {\r
81                 if (client != null && client.Self != null && !string.IsNullOrEmpty(client.Self.Name))\r
82                 {\r
83                     return Path.Combine(userDir, client.Self.Name);\r
84                 }\r
85                 else\r
86                 {\r
87                     return Environment.CurrentDirectory;\r
88                 }\r
89             }\r
90         }\r
91 \r
92         public string InventoryCacheFileName { get { return Path.Combine(ClientDir, "inventory.cache"); } }\r
93 \r
94         private string animCacheDir;\r
95         public string AnimCacheDir { get { return animCacheDir; } }\r
96 \r
97         private string globalLogFile;\r
98         public string GlobalLogFile { get { return globalLogFile; } }\r
99 \r
100         private bool monoRuntime;\r
101         public bool MonoRuntime { get { return monoRuntime; } }\r
102 \r
103         private Dictionary<UUID, Group> groups;\r
104         public Dictionary<UUID, Group> Groups { get { return groups; } }\r
105 \r
106         private Settings globalSettings;\r
107         /// <summary>\r
108         /// Global settings for the entire application\r
109         /// </summary>\r
110         public Settings GlobalSettings { get { return globalSettings; } }\r
111 \r
112         private Settings clientSettings;\r
113         /// <summary>\r
114         /// Per client settings\r
115         /// </summary>\r
116         public Settings ClientSettings { get { return clientSettings; } }\r
117 \r
118         public Dictionary<UUID, string> nameCache = new Dictionary<UUID, string>();\r
119 \r
120         public const string INCOMPLETE_NAME = "Loading...";\r
121 \r
122         public readonly bool advancedDebugging = false;\r
123 \r
124         public readonly List<IRadegastPlugin> PluginsLoaded = new List<IRadegastPlugin>();\r
125 \r
126         private MediaManager mediaManager;\r
127         /// <summary>\r
128         /// Radegast media manager for playing streams and in world sounds\r
129         /// </summary>\r
130         public MediaManager MediaManager { get { return mediaManager; } }\r
131 \r
132 \r
133         private CommandsManager commandsManager;\r
134         /// <summary>\r
135         /// Radegast command manager for executing textual console commands\r
136         /// </summary>\r
137         public CommandsManager CommandsManager { get { return commandsManager; } }\r
138 \r
139         /// <summary>\r
140         /// Radegast ContextAction manager for context sensitive actions\r
141         /// </summary>\r
142         public ContextActionsManager ContextActionManager { get; private set; }\r
143 \r
144         private SleekMovement movement;\r
145         /// <summary>\r
146         /// Allows key emulation for moving avatar around\r
147         /// </summary>\r
148         public SleekMovement Movement { get { return movement; } }\r
149 \r
150         public RadegastInstance(GridClient client0)\r
151         {\r
152             // incase something else calls GlobalInstance while we are loading\r
153             globalInstance = this;\r
154             Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);\r
155             Application.ThreadException += HandleThreadException;\r
156             client = client0;\r
157 \r
158             // Are we running mono?\r
159             monoRuntime = Type.GetType("Mono.Runtime") != null;\r
160 \r
161             netcom = new RadegastNetcom(client);\r
162             state = new StateManager(this);\r
163             mediaManager = new MediaManager(this);\r
164             commandsManager = new CommandsManager(this);\r
165             ContextActionManager = new ContextActionsManager(this);\r
166             movement = new SleekMovement(client);\r
167 \r
168             InitializeLoggingAndConfig();\r
169 \r
170             client.Settings.MULTIPLE_SIMS = true;\r
171 \r
172             client.Settings.USE_INTERPOLATION_TIMER = false;\r
173             client.Settings.ALWAYS_REQUEST_OBJECTS = true;\r
174             client.Settings.ALWAYS_DECODE_OBJECTS = true;\r
175             client.Settings.OBJECT_TRACKING = true;\r
176             client.Settings.ENABLE_SIMSTATS = true;\r
177             client.Settings.FETCH_MISSING_INVENTORY = true;\r
178             client.Settings.SEND_AGENT_THROTTLE = true;\r
179             client.Settings.SEND_AGENT_UPDATES = true;\r
180 \r
181             client.Settings.USE_ASSET_CACHE = true;\r
182             client.Settings.ASSET_CACHE_DIR = Path.Combine(userDir, "cache");\r
183             client.Assets.Cache.AutoPruneEnabled = false;\r
184 \r
185             client.Throttle.Texture = 2446000.0f;\r
186             client.Throttle.Asset = 2446000.0f;\r
187             client.Settings.THROTTLE_OUTGOING_PACKETS = true;\r
188             client.Settings.LOGIN_TIMEOUT = 120 * 1000;\r
189             client.Settings.SIMULATOR_TIMEOUT = 120 * 1000;\r
190             client.Settings.MAX_CONCURRENT_TEXTURE_DOWNLOADS = 20;\r
191 \r
192             mainForm = new frmMain(this);\r
193             mainForm.InitializeControls();\r
194             tabsConsole = mainForm.TabConsole;\r
195 \r
196             groups = new Dictionary<UUID, Group>();\r
197 \r
198             client.Groups.OnCurrentGroups += new GroupManager.CurrentGroupsCallback(Groups_OnCurrentGroups);\r
199             client.Groups.OnGroupLeft += new GroupManager.GroupLeftCallback(Groups_OnGroupLeft);\r
200             client.Groups.OnGroupDropped += new GroupManager.GroupDroppedCallback(Groups_OnGroupDropped);\r
201             client.Groups.OnGroupJoined += new GroupManager.GroupJoinedCallback(Groups_OnGroupJoined);\r
202             client.Avatars.OnAvatarNames += new AvatarManager.AvatarNamesCallback(Avatars_OnAvatarNames);\r
203             client.Network.OnConnected += new NetworkManager.ConnectedCallback(Network_OnConnected);\r
204             ScanAndLoadPlugins();\r
205         }\r
206 \r
207         public void CleanUp()\r
208         {\r
209             if (client != null)\r
210             {\r
211                 client.Groups.OnCurrentGroups -= new GroupManager.CurrentGroupsCallback(Groups_OnCurrentGroups);\r
212                 client.Groups.OnGroupLeft -= new GroupManager.GroupLeftCallback(Groups_OnGroupLeft);\r
213                 client.Groups.OnGroupDropped -= new GroupManager.GroupDroppedCallback(Groups_OnGroupDropped);\r
214                 client.Groups.OnGroupJoined -= new GroupManager.GroupJoinedCallback(Groups_OnGroupJoined);\r
215                 client.Avatars.OnAvatarNames -= new AvatarManager.AvatarNamesCallback(Avatars_OnAvatarNames);\r
216                 client.Network.OnConnected -= new NetworkManager.ConnectedCallback(Network_OnConnected);\r
217             }\r
218 \r
219             lock (PluginsLoaded)\r
220             {\r
221                  List<IRadegastPlugin> unload = new List<IRadegastPlugin>(PluginsLoaded);\r
222                  unload.ForEach(plug =>\r
223                 {\r
224                     PluginsLoaded.Remove(plug);\r
225                     try\r
226                     {\r
227                         plug.StopPlugin(this);\r
228                     }\r
229                     catch (Exception ex)\r
230                     {\r
231                         Logger.Log("ERROR in Shutdown Plugin: " + plug + " because " + ex, Helpers.LogLevel.Debug, ex);\r
232                     }\r
233                 });\r
234             }\r
235 \r
236             if (movement != null)\r
237             {\r
238                 movement.Dispose();\r
239                 movement = null;\r
240             }\r
241             if (commandsManager != null)\r
242             {\r
243                 commandsManager.Dispose();\r
244                 commandsManager = null;\r
245             }\r
246             if (ContextActionManager != null)\r
247             {\r
248                 ContextActionManager.Dispose();\r
249                 ContextActionManager = null;\r
250             }\r
251             if (mediaManager != null)\r
252             {\r
253                 mediaManager.Dispose();\r
254                 mediaManager = null;\r
255             }\r
256             if (state != null)\r
257             {\r
258                 state.Dispose();\r
259                 state = null;\r
260             }\r
261             if (netcom != null)\r
262             {\r
263                 netcom.Dispose();\r
264                 netcom = null;\r
265             }\r
266 \r
267             Logger.Log("RadegastInstance finished cleaning up.", Helpers.LogLevel.Debug);\r
268         }\r
269 \r
270         private void ScanAndLoadPlugins()\r
271         {\r
272             string dirName = Application.StartupPath;\r
273 \r
274             if (!Directory.Exists(dirName)) return;\r
275 \r
276             foreach (string loadfilename in Directory.GetFiles(dirName))\r
277             {\r
278                 if (loadfilename.ToLower().EndsWith(".dll") || loadfilename.ToLower().EndsWith(".exe"))\r
279                 {\r
280                     try\r
281                     {\r
282                         Assembly assembly = Assembly.LoadFile(loadfilename);\r
283                         LoadAssembly(loadfilename, assembly);\r
284                     }\r
285                     catch (BadImageFormatException)\r
286                     {\r
287                         // non .NET .dlls\r
288                     }\r
289                     catch (ReflectionTypeLoadException)\r
290                     {\r
291                         // Out of date or dlls missing sub dependencies\r
292                     }\r
293                     catch (Exception ex)\r
294                     {\r
295                         Logger.Log("ERROR in Radegast Plugin: " + loadfilename + " because " + ex.Message + " " + ex.StackTrace, Helpers.LogLevel.Debug);\r
296                     }\r
297                 }\r
298             }\r
299         }\r
300 \r
301         public void LoadAssembly(string loadfilename, Assembly assembly)\r
302         {\r
303             foreach (Type type in assembly.GetTypes())\r
304             {\r
305                 if (typeof(IRadegastPlugin).IsAssignableFrom(type))\r
306                 {\r
307                     foreach (var ci in type.GetConstructors())\r
308                     {\r
309                         if (ci.GetParameters().Length > 0) continue;\r
310                         try\r
311                         {\r
312                             IRadegastPlugin plug = (IRadegastPlugin)ci.Invoke(new object[0]);\r
313                             plug.StartPlugin(this);\r
314                             lock (PluginsLoaded) PluginsLoaded.Add(plug);\r
315                             break;\r
316                         }\r
317                         catch (Exception ex)\r
318                         {\r
319                             Logger.Log("ERROR Constructing Radegast Plugin: " + loadfilename + " because " + ex.Message, Helpers.LogLevel.Debug);\r
320                             throw ex;\r
321                         }\r
322                     }\r
323                 }\r
324                 else\r
325                 {\r
326                     try\r
327                     {\r
328                         commandsManager.LoadType(type);\r
329                         ContextActionManager.LoadType(type);\r
330                     }\r
331                     catch (Exception ex)\r
332                     {\r
333                         Logger.Log("ERROR in Radegast Plugin: " + loadfilename + " Command: " + type +\r
334                                    " because " + ex.Message + " " + ex.StackTrace, Helpers.LogLevel.Debug);\r
335                     }\r
336                 }\r
337             }\r
338         }\r
339 \r
340         void Network_OnConnected(object sender)\r
341         {\r
342             try\r
343             {\r
344                 if (!Directory.Exists(ClientDir))\r
345                     Directory.CreateDirectory(ClientDir);\r
346             }\r
347             catch (Exception ex)\r
348             {\r
349                 Logger.Log("Failed to create client directory", Helpers.LogLevel.Warning, ex);\r
350             }\r
351 \r
352             clientSettings = new Settings(Path.Combine(ClientDir, "client_settings.xml"));\r
353         }\r
354 \r
355         void Avatars_OnAvatarNames(Dictionary<UUID, string> names)\r
356         {\r
357             lock (nameCache)\r
358             {\r
359                 foreach (KeyValuePair<UUID, string> av in names)\r
360                 {\r
361                     if (!nameCache.ContainsKey(av.Key))\r
362                     {\r
363                         nameCache.Add(av.Key, av.Value);\r
364                     }\r
365                 }\r
366             }\r
367         }\r
368 \r
369         public string getAvatarName(UUID key)\r
370         {\r
371             lock (nameCache)\r
372             {\r
373                 if (key == UUID.Zero)\r
374                 {\r
375                     return "(???) (???)";\r
376                 }\r
377                 if (nameCache.ContainsKey(key))\r
378                 {\r
379                     return nameCache[key];\r
380                 }\r
381                 else\r
382                 {\r
383                     client.Avatars.RequestAvatarName(key);\r
384                     return INCOMPLETE_NAME;\r
385                 }\r
386             }\r
387         }\r
388 \r
389         public void getAvatarNames(List<UUID> keys)\r
390         {\r
391             lock (nameCache)\r
392             {\r
393                 List<UUID> newNames = new List<UUID>();\r
394                 foreach (UUID key in keys)\r
395                 {\r
396                     if (!nameCache.ContainsKey(key))\r
397                     {\r
398                         newNames.Add(key);\r
399                     }\r
400                 }\r
401                 if (newNames.Count > 0)\r
402                 {\r
403                     client.Avatars.RequestAvatarNames(newNames);\r
404                 }\r
405             }\r
406         }\r
407 \r
408         public bool haveAvatarName(UUID key)\r
409         {\r
410             lock (nameCache)\r
411             {\r
412                 if (nameCache.ContainsKey(key))\r
413                     return true;\r
414                 else\r
415                     return false;\r
416             }\r
417         }\r
418 \r
419         void Groups_OnGroupJoined(UUID groupID, bool success)\r
420         {\r
421             client.Groups.RequestCurrentGroups();\r
422         }\r
423 \r
424         void Groups_OnGroupLeft(UUID groupID, bool success)\r
425         {\r
426             client.Groups.RequestCurrentGroups();\r
427         }\r
428 \r
429         void Groups_OnGroupDropped(UUID groupID)\r
430         {\r
431             client.Groups.RequestCurrentGroups();\r
432         }\r
433 \r
434         public static string SafeFileName(string fileName)\r
435         {\r
436             foreach (char lDisallowed in Path.GetInvalidFileNameChars())\r
437             {\r
438                 fileName = fileName.Replace(lDisallowed.ToString(), "_");\r
439             }\r
440 \r
441             return fileName;\r
442         }\r
443 \r
444         public void LogClientMessage(string fileName, string message)\r
445         {\r
446             lock (this)\r
447             {\r
448                 try\r
449                 {\r
450                     foreach (char lDisallowed in System.IO.Path.GetInvalidFileNameChars())\r
451                     {\r
452                         fileName = fileName.Replace(lDisallowed.ToString(), "_");\r
453                     }\r
454 \r
455                     StreamWriter logfile = File.AppendText(Path.Combine(ClientDir, fileName));\r
456                     logfile.WriteLine(DateTime.Now.ToString("yyyy-MM-dd [HH:mm:ss] ") + message);\r
457                     logfile.Close();\r
458                     logfile.Dispose();\r
459                 }\r
460                 catch (Exception) { }\r
461             }\r
462         }\r
463 \r
464         void Groups_OnCurrentGroups(Dictionary<UUID, Group> gr)\r
465         {\r
466             this.groups = gr;\r
467         }\r
468 \r
469         private void InitializeLoggingAndConfig()\r
470         {\r
471             try\r
472             {\r
473                 userDir = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), Properties.Resources.ProgramName);\r
474                 if (!Directory.Exists(userDir))\r
475                 {\r
476                     Directory.CreateDirectory(userDir);\r
477                 }\r
478             }\r
479             catch (Exception)\r
480             {\r
481                 userDir = System.Environment.CurrentDirectory;\r
482             };\r
483 \r
484             animCacheDir = Path.Combine(userDir, @"anim_cache");\r
485             globalLogFile = Path.Combine(userDir, Properties.Resources.ProgramName + ".log");\r
486             globalSettings = new Settings(Path.Combine(userDir, "settings.xml"));\r
487         }\r
488 \r
489         public GridClient Client\r
490         {\r
491             get { return client; }\r
492         }\r
493 \r
494         public RadegastNetcom Netcom\r
495         {\r
496             get { return netcom; }\r
497         }\r
498 \r
499         public StateManager State\r
500         {\r
501             get { return state; }\r
502         }\r
503 \r
504         public frmMain MainForm\r
505         {\r
506             get { return mainForm; }\r
507         }\r
508 \r
509         public TabsConsole TabConsole\r
510         {\r
511             get { return tabsConsole; }\r
512         }\r
513 \r
514         public void HandleThreadException(object sender, ThreadExceptionEventArgs e)\r
515         {\r
516             Logger.Log("Unhandled Thread Exception: "+e.Exception + " in " +sender,Helpers.LogLevel.Error,client);\r
517         }\r
518     }\r
519 }\r