OpenSimBase.cs 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSim Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.IO;
  30. using System.Net;
  31. using System.Reflection;
  32. using System.Text;
  33. using System.Threading;
  34. using OpenMetaverse;
  35. using log4net;
  36. using Nini.Config;
  37. using OpenSim.Framework;
  38. using OpenSim.Framework.Communications;
  39. using OpenSim.Framework.Communications.Cache;
  40. using OpenSim.Framework.Console;
  41. using OpenSim.Framework.Servers;
  42. using OpenSim.Framework.Statistics;
  43. using OpenSim.Region.ClientStack;
  44. using OpenSim.Region.Communications.Local;
  45. using OpenSim.Region.Communications.OGS1;
  46. using OpenSim.Region.Environment;
  47. using OpenSim.Region.Environment.Interfaces;
  48. using OpenSim.Region.Environment.Scenes;
  49. using OpenSim.Region.Physics.Manager;
  50. namespace OpenSim
  51. {
  52. /// <summary>
  53. /// Common OpenSim region service code
  54. /// </summary>
  55. public class OpenSimBase : RegionApplicationBase
  56. {
  57. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  58. protected string proxyUrl;
  59. protected int proxyOffset = 0;
  60. /// <summary>
  61. /// The file used to load and save prim backup xml if no filename has been specified
  62. /// </summary>
  63. protected const string DEFAULT_PRIM_BACKUP_FILENAME = "prim-backup.xml";
  64. /// <summary>
  65. /// The file used to load and save an opensim archive if no filename has been specified
  66. /// </summary>
  67. protected const string DEFAULT_OAR_BACKUP_FILENAME = "scene_oar.tar.gz";
  68. /// <summary>
  69. /// The file to load and save inventory if no filename has been specified
  70. /// </summary>
  71. protected const string DEFAULT_INV_BACKUP_FILENAME = "opensim_inv.tar.gz";
  72. protected ConfigSettings m_configSettings;
  73. public ConfigSettings ConfigurationSettings
  74. {
  75. get { return m_configSettings; }
  76. set { m_configSettings = value; }
  77. }
  78. protected ConfigurationLoader m_configLoader;
  79. protected GridInfoService m_gridInfoService;
  80. protected List<IClientNetworkServer> m_clientServers = new List<IClientNetworkServer>();
  81. protected List<RegionInfo> m_regionData = new List<RegionInfo>();
  82. public ConsoleCommand CreateAccount = null;
  83. protected List<IApplicationPlugin> m_plugins = new List<IApplicationPlugin>();
  84. /// <value>
  85. /// The config information passed into the OpenSim region server.
  86. /// </value>
  87. public OpenSimConfigSource ConfigSource
  88. {
  89. get { return m_config; }
  90. set { m_config = value; }
  91. }
  92. protected OpenSimConfigSource m_config;
  93. public List<IClientNetworkServer> ClientServers
  94. {
  95. get { return m_clientServers; }
  96. }
  97. public List<RegionInfo> RegionData
  98. {
  99. get { return m_regionData; }
  100. }
  101. public new BaseHttpServer HttpServer
  102. {
  103. get { return m_httpServer; }
  104. }
  105. public uint HttpServerPort
  106. {
  107. get { return m_httpServerPort; }
  108. }
  109. protected ModuleLoader m_moduleLoader;
  110. public ModuleLoader ModuleLoader
  111. {
  112. get { return m_moduleLoader; }
  113. set { m_moduleLoader = value; }
  114. }
  115. /// <summary>
  116. /// Constructor.
  117. /// </summary>
  118. /// <param name="configSource"></param>
  119. public OpenSimBase(IConfigSource configSource) : base()
  120. {
  121. LoadConfigSettings(configSource);
  122. }
  123. protected virtual void LoadConfigSettings(IConfigSource configSource)
  124. {
  125. m_configLoader = new ConfigurationLoader();
  126. m_config = m_configLoader.LoadConfigSettings(configSource, out m_configSettings, out m_networkServersInfo);
  127. ReadExtraConfigSettings();
  128. }
  129. protected virtual void ReadExtraConfigSettings()
  130. {
  131. IConfig networkConfig = m_config.Source.Configs["Network"];
  132. if (networkConfig != null)
  133. {
  134. proxyUrl = networkConfig.GetString("proxy_url", "");
  135. proxyOffset = Int32.Parse(networkConfig.GetString("proxy_offset", "0"));
  136. }
  137. }
  138. protected virtual void LoadPlugins()
  139. {
  140. PluginLoader<IApplicationPlugin> loader =
  141. new PluginLoader<IApplicationPlugin>(new ApplicationPluginInitialiser(this));
  142. loader.Load("/OpenSim/Startup");
  143. m_plugins = loader.Plugins;
  144. }
  145. /// <summary>
  146. /// Performs startup specific to this region server, including initialization of the scene
  147. /// such as loading configuration from disk.
  148. /// </summary>
  149. protected override void StartupSpecific()
  150. {
  151. base.StartupSpecific();
  152. m_stats = StatsManager.StartCollectingSimExtraStats();
  153. LibraryRootFolder libraryRootFolder = new LibraryRootFolder(m_configSettings.LibrariesXMLFile);
  154. // StandAlone mode? is determined by !startupConfig.GetBoolean("gridmode", false)
  155. if (m_configSettings.Standalone)
  156. {
  157. InitialiseStandaloneServices(libraryRootFolder);
  158. }
  159. else
  160. {
  161. // We are in grid mode
  162. InitialiseGridServices(libraryRootFolder);
  163. }
  164. // Create a ModuleLoader instance
  165. m_moduleLoader = new ModuleLoader(m_config.Source);
  166. LoadPlugins();
  167. // Only enable logins to the regions once we have completely finished starting up (apart from scripts)
  168. m_commsManager.GridService.RegionLoginsEnabled = true;
  169. }
  170. /// <summary>
  171. /// Initialises the backend services for standalone mode, and registers some http handlers
  172. /// </summary>
  173. /// <param name="libraryRootFolder"></param>
  174. protected virtual void InitialiseStandaloneServices(LibraryRootFolder libraryRootFolder)
  175. {
  176. LocalInventoryService inventoryService = new LocalInventoryService();
  177. inventoryService.AddPlugin(m_configSettings.StandaloneInventoryPlugin, m_configSettings.StandaloneInventorySource);
  178. LocalUserServices userService =
  179. new LocalUserServices(
  180. m_networkServersInfo.DefaultHomeLocX, m_networkServersInfo.DefaultHomeLocY, inventoryService);
  181. userService.AddPlugin(m_configSettings.StandaloneUserPlugin, m_configSettings.StandaloneUserSource);
  182. LocalBackEndServices backendService = new LocalBackEndServices();
  183. LocalLoginService loginService =
  184. new LocalLoginService(
  185. userService, m_configSettings.StandaloneWelcomeMessage, inventoryService, backendService, m_networkServersInfo,
  186. m_configSettings.StandaloneAuthenticate, libraryRootFolder);
  187. m_commsManager
  188. = new CommunicationsLocal(
  189. m_networkServersInfo, m_httpServer, m_assetCache, userService, userService,
  190. inventoryService, backendService, backendService, userService,
  191. libraryRootFolder, m_configSettings.DumpAssetsToFile);
  192. // set up XMLRPC handler for client's initial login request message
  193. m_httpServer.AddXmlRPCHandler("login_to_simulator", loginService.XmlRpcLoginMethod);
  194. // provides the web form login
  195. m_httpServer.AddHTTPHandler("login", loginService.ProcessHTMLLogin);
  196. // Provides the LLSD login
  197. m_httpServer.SetDefaultLLSDHandler(loginService.LLSDLoginMethod);
  198. // provide grid info
  199. // m_gridInfoService = new GridInfoService(m_config.Source.Configs["Startup"].GetString("inifile", Path.Combine(Util.configDir(), "OpenSim.ini")));
  200. m_gridInfoService = new GridInfoService(m_config.Source);
  201. m_httpServer.AddXmlRPCHandler("get_grid_info", m_gridInfoService.XmlRpcGridInfoMethod);
  202. m_httpServer.AddStreamHandler(new RestStreamHandler("GET", "/get_grid_info", m_gridInfoService.RestGetGridInfoMethod));
  203. }
  204. protected virtual void InitialiseGridServices(LibraryRootFolder libraryRootFolder)
  205. {
  206. m_commsManager
  207. = new CommunicationsOGS1(m_networkServersInfo, m_httpServer, m_assetCache, libraryRootFolder);
  208. m_httpServer.AddStreamHandler(new SimStatusHandler());
  209. }
  210. protected override void Initialize()
  211. {
  212. //
  213. // Called from base.StartUp()
  214. //
  215. m_httpServerPort = m_networkServersInfo.HttpListenerPort;
  216. InitialiseAssetCache();
  217. m_sceneManager.OnRestartSim += handleRestartRegion;
  218. }
  219. /// <summary>
  220. /// Initialises the assetcache
  221. /// </summary>
  222. protected virtual void InitialiseAssetCache()
  223. {
  224. IAssetServer assetServer;
  225. if (m_configSettings.AssetStorage == "grid")
  226. {
  227. assetServer = new GridAssetClient(m_networkServersInfo.AssetURL);
  228. }
  229. else if (m_configSettings.AssetStorage == "cryptogrid") // Decrypt-Only
  230. {
  231. assetServer = new CryptoGridAssetClient(m_networkServersInfo.AssetURL,
  232. Environment.CurrentDirectory, true);
  233. }
  234. else if (m_configSettings.AssetStorage == "cryptogrid_eou") // Encrypts All Assets
  235. {
  236. assetServer = new CryptoGridAssetClient(m_networkServersInfo.AssetURL,
  237. Environment.CurrentDirectory, false);
  238. }
  239. else if (m_configSettings.AssetStorage == "file")
  240. {
  241. assetServer = new FileAssetClient(m_networkServersInfo.AssetURL);
  242. }
  243. else
  244. {
  245. SQLAssetServer sqlAssetServer = new SQLAssetServer(m_configSettings.StandaloneAssetPlugin, m_configSettings.StandaloneAssetSource);
  246. sqlAssetServer.LoadDefaultAssets(m_configSettings.AssetSetsXMLFile);
  247. assetServer = sqlAssetServer;
  248. }
  249. m_assetCache = new AssetCache(assetServer);
  250. }
  251. public void ProcessLogin(bool LoginEnabled)
  252. {
  253. if (LoginEnabled)
  254. {
  255. m_log.Info("[Login] Login are now enabled ");
  256. m_commsManager.GridService.RegionLoginsEnabled = true;
  257. }
  258. else
  259. {
  260. m_log.Info("[Login] Login are now disabled ");
  261. m_commsManager.GridService.RegionLoginsEnabled = false;
  262. }
  263. }
  264. /// <summary>
  265. /// Execute the region creation process. This includes setting up scene infrastructure.
  266. /// </summary>
  267. /// <param name="regionInfo"></param>
  268. /// <param name="portadd_flag"></param>
  269. /// <returns></returns>
  270. public IClientNetworkServer CreateRegion(RegionInfo regionInfo, bool portadd_flag)
  271. {
  272. return CreateRegion(regionInfo, portadd_flag, false);
  273. }
  274. /// <summary>
  275. /// Execute the region creation process. This includes setting up scene infrastructure.
  276. /// </summary>
  277. /// <param name="regionInfo"></param>
  278. /// <returns></returns>
  279. public IClientNetworkServer CreateRegion(RegionInfo regionInfo)
  280. {
  281. return CreateRegion(regionInfo, false, true);
  282. }
  283. /// <summary>
  284. /// Execute the region creation process. This includes setting up scene infrastructure.
  285. /// </summary>
  286. /// <param name="regionInfo"></param>
  287. /// <param name="portadd_flag"></param>
  288. /// <param name="do_post_init"></param>
  289. /// <returns></returns>
  290. public IClientNetworkServer CreateRegion(RegionInfo regionInfo, bool portadd_flag, bool do_post_init)
  291. {
  292. int port = regionInfo.InternalEndPoint.Port;
  293. // set initial originRegionID to RegionID in RegionInfo. (it needs for loding prims)
  294. regionInfo.originRegionID = regionInfo.RegionID;
  295. // set initial ServerURI
  296. regionInfo.ServerURI = "http://" + regionInfo.ExternalHostName + ":" + regionInfo.InternalEndPoint.Port;
  297. regionInfo.HttpPort = m_httpServerPort;
  298. if ((proxyUrl.Length > 0) && (portadd_flag))
  299. {
  300. // set proxy url to RegionInfo
  301. regionInfo.proxyUrl = proxyUrl;
  302. Util.XmlRpcCommand(proxyUrl, "AddPort", port, port + proxyOffset, regionInfo.ExternalHostName);
  303. }
  304. IClientNetworkServer clientServer;
  305. Scene scene = SetupScene(regionInfo, proxyOffset, m_config.Source, out clientServer);
  306. m_log.Info("[MODULES]: Loading Region's modules");
  307. List<IRegionModule> modules = m_moduleLoader.PickupModules(scene, ".");
  308. // This needs to be ahead of the script engine load, so the
  309. // script module can pick up events exposed by a module
  310. m_moduleLoader.InitialiseSharedModules(scene);
  311. scene.SetModuleInterfaces();
  312. // Prims have to be loaded after module configuration since some modules may be invoked during the load
  313. scene.LoadPrimsFromStorage(regionInfo.originRegionID);
  314. scene.StartTimer();
  315. // moved these here as the terrain texture has to be created after the modules are initialized
  316. // and has to happen before the region is registered with the grid.
  317. scene.CreateTerrainTexture(false);
  318. try
  319. {
  320. scene.RegisterRegionWithGrid();
  321. }
  322. catch (Exception e)
  323. {
  324. m_log.ErrorFormat("[STARTUP]: Registration of region with grid failed, aborting startup - {0}", e);
  325. // Carrying on now causes a lot of confusion down the
  326. // line - we need to get the user's attention
  327. System.Environment.Exit(1);
  328. }
  329. // We need to do this after we've initialized the
  330. // scripting engines.
  331. scene.CreateScriptInstances();
  332. scene.loadAllLandObjectsFromStorage(regionInfo.originRegionID);
  333. scene.EventManager.TriggerParcelPrimCountUpdate();
  334. m_sceneManager.Add(scene);
  335. m_clientServers.Add(clientServer);
  336. m_regionData.Add(regionInfo);
  337. clientServer.Start();
  338. if (do_post_init)
  339. {
  340. foreach (IRegionModule module in modules)
  341. {
  342. module.PostInitialise();
  343. }
  344. }
  345. return clientServer;
  346. }
  347. public void RemoveRegion(Scene scene, bool cleanup)
  348. {
  349. // only need to check this if we are not at the
  350. // root level
  351. if ((m_sceneManager.CurrentScene != null) &&
  352. (m_sceneManager.CurrentScene.RegionInfo.RegionID == scene.RegionInfo.RegionID))
  353. {
  354. m_sceneManager.TrySetCurrentScene("..");
  355. }
  356. scene.DeleteAllSceneObjects();
  357. m_regionData.Remove(scene.RegionInfo);
  358. m_sceneManager.CloseScene(scene);
  359. if (!cleanup)
  360. return;
  361. if (!String.IsNullOrEmpty(scene.RegionInfo.RegionFile))
  362. {
  363. File.Delete(scene.RegionInfo.RegionFile);
  364. m_log.InfoFormat("[OPENSIM MAIN] deleting region file \"{0}\"", scene.RegionInfo.RegionFile);
  365. }
  366. }
  367. public void RemoveRegion(string name, bool cleanUp)
  368. {
  369. Scene target;
  370. if (m_sceneManager.TryGetScene(name, out target))
  371. RemoveRegion(target, cleanUp);
  372. }
  373. protected override StorageManager CreateStorageManager()
  374. {
  375. return CreateStorageManager(m_configSettings.StorageConnectionString, m_configSettings.EstateConnectionString);
  376. }
  377. protected StorageManager CreateStorageManager(string connectionstring, string estateconnectionstring)
  378. {
  379. return new StorageManager(m_configSettings.StorageDll, connectionstring, estateconnectionstring);
  380. }
  381. protected override ClientStackManager CreateClientStackManager()
  382. {
  383. return new ClientStackManager(m_configSettings.ClientstackDll);
  384. }
  385. protected override Scene CreateScene(RegionInfo regionInfo, StorageManager storageManager,
  386. AgentCircuitManager circuitManager)
  387. {
  388. SceneCommunicationService sceneGridService = new SceneCommunicationService(m_commsManager);
  389. return
  390. new Scene(regionInfo, circuitManager, m_commsManager, sceneGridService, m_assetCache,
  391. storageManager, m_httpServer,
  392. m_moduleLoader, m_configSettings.DumpAssetsToFile, m_configSettings.PhysicalPrim, m_configSettings.See_into_region_from_neighbor, m_config.Source,
  393. m_version);
  394. }
  395. public void handleRestartRegion(RegionInfo whichRegion)
  396. {
  397. m_log.Error("[OPENSIM MAIN]: Got restart signal from SceneManager");
  398. // Shutting down the client server
  399. bool foundClientServer = false;
  400. int clientServerElement = 0;
  401. for (int i = 0; i < m_clientServers.Count; i++)
  402. {
  403. //--> Melanie, the following needs to be fixed
  404. // the Equals override is not returning true if the locations are actually equal
  405. if (m_clientServers[i].HandlesRegion(new Location(whichRegion.RegionHandle)))
  406. {
  407. clientServerElement = i;
  408. foundClientServer = true;
  409. break;
  410. }
  411. }
  412. if (foundClientServer)
  413. {
  414. m_clientServers[clientServerElement].Server.Close();
  415. m_clientServers.RemoveAt(clientServerElement);
  416. }
  417. //Removing the region from the sim's database of regions..
  418. int RegionHandleElement = -1;
  419. for (int i = 0; i < m_regionData.Count; i++)
  420. {
  421. if (whichRegion.RegionHandle == m_regionData[i].RegionHandle)
  422. {
  423. RegionHandleElement = i;
  424. }
  425. }
  426. if (RegionHandleElement >= 0)
  427. {
  428. m_regionData.RemoveAt(RegionHandleElement);
  429. }
  430. CreateRegion(whichRegion, true);
  431. }
  432. # region Setup methods
  433. protected override PhysicsScene GetPhysicsScene(string osSceneIdentifier)
  434. {
  435. return GetPhysicsScene(
  436. m_configSettings.PhysicsEngine, m_configSettings.MeshEngineName, m_config.Source, osSceneIdentifier);
  437. }
  438. /// <summary>
  439. /// Handler to supply the current status of this sim
  440. /// </summary>
  441. /// Currently this is always OK if the simulator is still listening for connections on its HTTP service
  442. protected class SimStatusHandler : IStreamedRequestHandler
  443. {
  444. public byte[] Handle(string path, Stream request,
  445. OSHttpRequest httpRequest, OSHttpResponse httpResponse)
  446. {
  447. return Encoding.UTF8.GetBytes("OK");
  448. }
  449. public string ContentType
  450. {
  451. get { return "text/plain"; }
  452. }
  453. public string HttpMethod
  454. {
  455. get { return "GET"; }
  456. }
  457. public string Path
  458. {
  459. get { return "/simstatus/"; }
  460. }
  461. }
  462. #endregion
  463. /// <summary>
  464. /// Performs any last-minute sanity checking and shuts down the region server
  465. /// </summary>
  466. public override void ShutdownSpecific()
  467. {
  468. if (proxyUrl.Length > 0)
  469. {
  470. Util.XmlRpcCommand(proxyUrl, "Stop");
  471. }
  472. m_log.Info("[SHUTDOWN]: Closing all threads");
  473. m_log.Info("[SHUTDOWN]: Killing listener thread");
  474. m_log.Info("[SHUTDOWN]: Killing clients");
  475. // TODO: implement this
  476. m_log.Info("[SHUTDOWN]: Closing console and terminating");
  477. try
  478. {
  479. m_sceneManager.Close();
  480. }
  481. catch (Exception e)
  482. {
  483. m_log.ErrorFormat("[SHUTDOWN]: Ignoring failure during shutdown - {0}", e);
  484. }
  485. }
  486. /// <summary>
  487. /// Get the start time and up time of Region server
  488. /// </summary>
  489. /// <param name="starttime">The first out parameter describing when the Region server started</param>
  490. /// <param name="uptime">The second out parameter describing how long the Region server has run</param>
  491. public void GetRunTime(out string starttime, out string uptime)
  492. {
  493. starttime = m_startuptime.ToString();
  494. uptime = (DateTime.Now - m_startuptime).ToString();
  495. }
  496. /// <summary>
  497. /// Get the number of the avatars in the Region server
  498. /// </summary>
  499. /// <param name="usernum">The first out parameter describing the number of all the avatars in the Region server</param>
  500. public void GetAvatarNumber(out int usernum)
  501. {
  502. usernum = m_sceneManager.GetCurrentSceneAvatars().Count;
  503. }
  504. /// <summary>
  505. /// Get the number of regions
  506. /// </summary>
  507. /// <param name="regionnum">The first out parameter describing the number of regions</param>
  508. public void GetRegionNumber(out int regionnum)
  509. {
  510. regionnum = m_sceneManager.Scenes.Count;
  511. }
  512. }
  513. public class OpenSimConfigSource
  514. {
  515. public IConfigSource Source;
  516. public void Save(string path)
  517. {
  518. if (Source is IniConfigSource)
  519. {
  520. IniConfigSource iniCon = (IniConfigSource)Source;
  521. iniCon.Save(path);
  522. }
  523. else if (Source is XmlConfigSource)
  524. {
  525. XmlConfigSource xmlCon = (XmlConfigSource)Source;
  526. xmlCon.Save(path);
  527. }
  528. }
  529. }
  530. }