OSDN Git Service

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