OSDN Git Service

Complete rewrite of the config system
[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.Windows.Forms;\r
36 using Radegast.Netcom;\r
37 using OpenMetaverse;\r
38 \r
39 namespace Radegast\r
40 {\r
41     public class RadegastInstance\r
42     {\r
43         private GridClient client;\r
44         private RadegastNetcom netcom;\r
45 \r
46         private StateManager state;\r
47 \r
48         private frmMain mainForm;\r
49         private TabsConsole tabsConsole;\r
50 \r
51         // Singleton, there can be only one instance\r
52         private static RadegastInstance globalInstance = null;\r
53         public static RadegastInstance GlobalInstance\r
54         {\r
55             get\r
56             {\r
57                 if (globalInstance == null)\r
58                 {\r
59                     globalInstance = new RadegastInstance(new GridClient());\r
60                 }\r
61                 return globalInstance;\r
62             }\r
63         }\r
64 \r
65         private string userDir;\r
66         /// <summary>\r
67         /// System (not grid!) user's dir\r
68         /// </summary>\r
69         public string UserDir { get { return userDir; } }\r
70 \r
71         /// <summary>\r
72         /// Grid client's user dir for settings and logs\r
73         /// </summary>\r
74         public string ClientDir\r
75         {\r
76             get\r
77             {\r
78                 if (client != null && client.Self != null && !string.IsNullOrEmpty(client.Self.Name))\r
79                 {\r
80                     return Path.Combine(userDir, client.Self.Name);\r
81                 }\r
82                 else\r
83                 {\r
84                     return Environment.CurrentDirectory;\r
85                 }\r
86             }\r
87         }\r
88 \r
89         public string InventoryCacheFileName { get { return Path.Combine(ClientDir, "inventory.cache"); } }\r
90 \r
91         private string animCacheDir;\r
92         public string AnimCacheDir { get { return animCacheDir; } }\r
93 \r
94         private string globalLogFile;\r
95         public string GlobalLogFile { get { return globalLogFile; } }\r
96 \r
97         private bool monoRuntime;\r
98         public bool MonoRuntime { get { return monoRuntime; } }\r
99 \r
100         private Dictionary<UUID, Group> groups;\r
101         public Dictionary<UUID, Group> Groups { get { return groups; } }\r
102 \r
103         private Settings globalSettings;\r
104         /// <summary>\r
105         /// Global settings for the entire application\r
106         /// </summary>\r
107         public Settings GlobalSettings { get { return globalSettings; } }\r
108 \r
109         private Settings clientSettings;\r
110         /// <summary>\r
111         /// Per client settings\r
112         /// </summary>\r
113         public Settings ClientSettings { get { return clientSettings; } }\r
114 \r
115         public Dictionary<UUID, string> nameCache = new Dictionary<UUID, string>();\r
116 \r
117         public const string INCOMPLETE_NAME = "Loading...";\r
118 \r
119         public readonly bool advancedDebugging = false;\r
120 \r
121         public readonly List<IRadegastPlugin> PluginsLoaded = new List<IRadegastPlugin>();\r
122 \r
123         public RadegastInstance(GridClient client0)\r
124         {\r
125             // incase something else calls GlobalInstance while we are loading\r
126             globalInstance = this; \r
127 \r
128             client = client0;\r
129 \r
130             netcom = new RadegastNetcom(client);\r
131             state = new StateManager(this);\r
132             InitializeLoggingAndConfig();\r
133 \r
134             client.Settings.MULTIPLE_SIMS = true;\r
135 \r
136             client.Settings.USE_INTERPOLATION_TIMER = false;\r
137             client.Settings.ALWAYS_REQUEST_OBJECTS = true;\r
138             client.Settings.ALWAYS_DECODE_OBJECTS = true;\r
139             client.Settings.OBJECT_TRACKING = true;\r
140             client.Settings.ENABLE_SIMSTATS = true;\r
141             client.Settings.FETCH_MISSING_INVENTORY = true;\r
142             client.Settings.SEND_AGENT_THROTTLE = true;\r
143             client.Settings.SEND_AGENT_UPDATES = true;\r
144 \r
145             client.Settings.USE_ASSET_CACHE = true;\r
146             client.Settings.ASSET_CACHE_DIR = Path.Combine(userDir, "cache");\r
147             client.Assets.Cache.AutoPruneEnabled = false;\r
148 \r
149             client.Throttle.Texture = 2446000.0f;\r
150             client.Throttle.Asset = 2446000.0f;\r
151             client.Settings.THROTTLE_OUTGOING_PACKETS = true;\r
152             client.Settings.LOGIN_TIMEOUT = 120 * 1000;\r
153             client.Settings.SIMULATOR_TIMEOUT = 120 * 1000;\r
154             client.Settings.MAX_CONCURRENT_TEXTURE_DOWNLOADS = 20;\r
155 \r
156             mainForm = new frmMain(this);\r
157             mainForm.InitializeControls();\r
158             tabsConsole = mainForm.TabConsole;\r
159 \r
160             groups = new Dictionary<UUID, Group>();\r
161 \r
162             client.Groups.OnCurrentGroups += new GroupManager.CurrentGroupsCallback(Groups_OnCurrentGroups);\r
163             client.Groups.OnGroupLeft += new GroupManager.GroupLeftCallback(Groups_OnGroupLeft);\r
164             client.Groups.OnGroupDropped += new GroupManager.GroupDroppedCallback(Groups_OnGroupDropped);\r
165             client.Groups.OnGroupJoined += new GroupManager.GroupJoinedCallback(Groups_OnGroupJoined);\r
166             client.Avatars.OnAvatarNames += new AvatarManager.AvatarNamesCallback(Avatars_OnAvatarNames);\r
167             client.Network.OnConnected += new NetworkManager.ConnectedCallback(Network_OnConnected);\r
168             ScanAndLoadPlugins();\r
169         }\r
170 \r
171         public void CleanUp()\r
172         {\r
173             if (client != null)\r
174             {\r
175                 client.Groups.OnCurrentGroups -= new GroupManager.CurrentGroupsCallback(Groups_OnCurrentGroups);\r
176                 client.Groups.OnGroupLeft -= new GroupManager.GroupLeftCallback(Groups_OnGroupLeft);\r
177                 client.Groups.OnGroupDropped -= new GroupManager.GroupDroppedCallback(Groups_OnGroupDropped);\r
178                 client.Groups.OnGroupJoined -= new GroupManager.GroupJoinedCallback(Groups_OnGroupJoined);\r
179                 client.Avatars.OnAvatarNames -= new AvatarManager.AvatarNamesCallback(Avatars_OnAvatarNames);\r
180                 client.Network.OnConnected -= new NetworkManager.ConnectedCallback(Network_OnConnected);\r
181             }\r
182 \r
183             lock (PluginsLoaded)\r
184             {\r
185                 PluginsLoaded.ForEach(plug =>\r
186                 {\r
187                     try\r
188                     {\r
189                         plug.StopPlugin(this);\r
190                     }\r
191                     catch (Exception) { }\r
192                 });\r
193             }\r
194 \r
195             state.Dispose();\r
196             state = null;\r
197             netcom.Dispose();\r
198             netcom = null;\r
199             Logger.Log("RadegastInstance finished cleaning up.", Helpers.LogLevel.Debug);\r
200         }\r
201 \r
202         private void ScanAndLoadPlugins()\r
203         {\r
204             string dirName = Application.StartupPath;\r
205 \r
206             if (!Directory.Exists(dirName)) return;\r
207 \r
208             foreach (string loadfilename in Directory.GetFiles(dirName))\r
209             {\r
210                 if (loadfilename.ToLower().EndsWith(".dll") || loadfilename.ToLower().EndsWith(".exe"))\r
211                 {\r
212                     try\r
213                     {\r
214                         Assembly assembly = Assembly.LoadFile(loadfilename);\r
215                         foreach (Type type in assembly.GetTypes())\r
216                         {\r
217                             if (typeof(IRadegastPlugin).IsAssignableFrom(type))\r
218                             {\r
219                                 foreach (var ci in type.GetConstructors())\r
220                                 {\r
221                                     if (ci.GetParameters().Length > 0) continue;\r
222                                     try\r
223                                     {\r
224                                         IRadegastPlugin plug = (IRadegastPlugin)ci.Invoke(new object[0]);\r
225                                         plug.StartPlugin(this);\r
226                                         lock (PluginsLoaded) PluginsLoaded.Add(plug);\r
227                                         break;\r
228                                     }\r
229                                     catch (Exception ex)\r
230                                     {\r
231                                         Logger.Log("ERROR in Radegast Plugin: " + ex.Message, Helpers.LogLevel.Debug);\r
232                                     }\r
233                                 }\r
234                             }\r
235                         }\r
236                     }\r
237                     catch (BadImageFormatException)\r
238                     {\r
239                         // non .NET .dlls\r
240                     }\r
241                     catch (ReflectionTypeLoadException)\r
242                     {\r
243                         // Out of date or dlls missing sub dependencies\r
244                     }\r
245                 }\r
246             }\r
247         }\r
248 \r
249         void Network_OnConnected(object sender)\r
250         {\r
251             try\r
252             {\r
253                 if (!Directory.Exists(ClientDir))\r
254                     Directory.CreateDirectory(ClientDir);\r
255             }\r
256             catch (Exception ex)\r
257             {\r
258                 Logger.Log("Failed to create client directory", Helpers.LogLevel.Warning, ex);\r
259             }\r
260 \r
261             clientSettings = new Settings(Path.Combine(ClientDir, "client_settings.xml"));\r
262         }\r
263 \r
264         void Avatars_OnAvatarNames(Dictionary<UUID, string> names)\r
265         {\r
266             lock (nameCache)\r
267             {\r
268                 foreach (KeyValuePair<UUID, string> av in names)\r
269                 {\r
270                     if (!nameCache.ContainsKey(av.Key))\r
271                     {\r
272                         nameCache.Add(av.Key, av.Value);\r
273                     }\r
274                 }\r
275             }\r
276         }\r
277 \r
278         public string getAvatarName(UUID key)\r
279         {\r
280             lock (nameCache)\r
281             {\r
282                 if (key == UUID.Zero)\r
283                 {\r
284                     return "(???) (???)";\r
285                 }\r
286                 if (nameCache.ContainsKey(key))\r
287                 {\r
288                     return nameCache[key];\r
289                 }\r
290                 else\r
291                 {\r
292                     client.Avatars.RequestAvatarName(key);\r
293                     return INCOMPLETE_NAME;\r
294                 }\r
295             }\r
296         }\r
297 \r
298         public void getAvatarNames(List<UUID> keys)\r
299         {\r
300             lock (nameCache)\r
301             {\r
302                 List<UUID> newNames = new List<UUID>();\r
303                 foreach (UUID key in keys)\r
304                 {\r
305                     if (!nameCache.ContainsKey(key))\r
306                     {\r
307                         newNames.Add(key);\r
308                     }\r
309                 }\r
310                 if (newNames.Count > 0)\r
311                 {\r
312                     client.Avatars.RequestAvatarNames(newNames);\r
313                 }\r
314             }\r
315         }\r
316 \r
317         public bool haveAvatarName(UUID key)\r
318         {\r
319             lock (nameCache)\r
320             {\r
321                 if (nameCache.ContainsKey(key))\r
322                     return true;\r
323                 else\r
324                     return false;\r
325             }\r
326         }\r
327 \r
328         void Groups_OnGroupJoined(UUID groupID, bool success)\r
329         {\r
330             client.Groups.RequestCurrentGroups();\r
331         }\r
332 \r
333         void Groups_OnGroupLeft(UUID groupID, bool success)\r
334         {\r
335             client.Groups.RequestCurrentGroups();\r
336         }\r
337 \r
338         void Groups_OnGroupDropped(UUID groupID)\r
339         {\r
340             client.Groups.RequestCurrentGroups();\r
341         }\r
342 \r
343         public static string SafeFileName(string fileName)\r
344         {\r
345             foreach (char lDisallowed in Path.GetInvalidFileNameChars())\r
346             {\r
347                 fileName = fileName.Replace(lDisallowed.ToString(), "_");\r
348             }\r
349 \r
350             return fileName;\r
351         }\r
352 \r
353         public void LogClientMessage(string fileName, string message)\r
354         {\r
355             lock (this)\r
356             {\r
357                 try\r
358                 {\r
359                     foreach (char lDisallowed in System.IO.Path.GetInvalidFileNameChars())\r
360                     {\r
361                         fileName = fileName.Replace(lDisallowed.ToString(), "_");\r
362                     }\r
363 \r
364                     StreamWriter logfile = File.AppendText(Path.Combine(ClientDir, fileName));\r
365                     logfile.WriteLine(DateTime.Now.ToString("yyyy-MM-dd [HH:mm:ss] ") + message);\r
366                     logfile.Close();\r
367                     logfile.Dispose();\r
368                 }\r
369                 catch (Exception) { }\r
370             }\r
371         }\r
372 \r
373         void Groups_OnCurrentGroups(Dictionary<UUID, Group> gr)\r
374         {\r
375             this.groups = gr;\r
376         }\r
377 \r
378         private void InitializeLoggingAndConfig()\r
379         {\r
380             // Are we running mono?\r
381             monoRuntime = Type.GetType("Mono.Runtime") != null;\r
382 \r
383             try\r
384             {\r
385                 userDir = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), Properties.Resources.ProgramName);\r
386                 if (!Directory.Exists(userDir))\r
387                 {\r
388                     Directory.CreateDirectory(userDir);\r
389                 }\r
390             }\r
391             catch (Exception)\r
392             {\r
393                 userDir = System.Environment.CurrentDirectory;\r
394             };\r
395 \r
396             animCacheDir = Path.Combine(userDir, @"anim_cache");\r
397             globalLogFile = Path.Combine(userDir, Properties.Resources.ProgramName + ".log");\r
398             globalSettings = new Settings(Path.Combine(userDir, "settings.xml"));\r
399         }\r
400 \r
401         public GridClient Client\r
402         {\r
403             get { return client; }\r
404         }\r
405 \r
406         public RadegastNetcom Netcom\r
407         {\r
408             get { return netcom; }\r
409         }\r
410 \r
411         public StateManager State\r
412         {\r
413             get { return state; }\r
414         }\r
415 \r
416         public frmMain MainForm\r
417         {\r
418             get { return mainForm; }\r
419         }\r
420 \r
421         public TabsConsole TabConsole\r
422         {\r
423             get { return tabsConsole; }\r
424         }\r
425     }\r
426 }\r