clockwerk-opensim – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | vero | 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 OpenSimulator 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 | |||
28 | using System; |
||
29 | using System.Collections.Generic; |
||
30 | using System.IO; |
||
31 | using System.Linq; |
||
32 | using System.Net; |
||
33 | using System.Reflection; |
||
34 | using System.Text; |
||
35 | using log4net; |
||
36 | using Nini.Config; |
||
37 | using OpenMetaverse; |
||
38 | using OpenSim.Framework; |
||
39 | using OpenSim.Framework.Communications; |
||
40 | using OpenSim.Framework.Console; |
||
41 | using OpenSim.Framework.Servers; |
||
42 | using OpenSim.Framework.Servers.HttpServer; |
||
43 | using OpenSim.Framework.Monitoring; |
||
44 | using OpenSim.Region.ClientStack; |
||
45 | using OpenSim.Region.CoreModules.ServiceConnectorsOut.UserAccounts; |
||
46 | using OpenSim.Region.Framework; |
||
47 | using OpenSim.Region.Framework.Interfaces; |
||
48 | using OpenSim.Region.Framework.Scenes; |
||
49 | using OpenSim.Region.Physics.Manager; |
||
50 | using OpenSim.Server.Base; |
||
51 | using OpenSim.Services.Base; |
||
52 | using OpenSim.Services.Interfaces; |
||
53 | using OpenSim.Services.UserAccountService; |
||
54 | |||
55 | namespace OpenSim |
||
56 | { |
||
57 | /// <summary> |
||
58 | /// Common OpenSimulator simulator code |
||
59 | /// </summary> |
||
60 | public class OpenSimBase : RegionApplicationBase |
||
61 | { |
||
62 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
||
63 | |||
64 | // These are the names of the plugin-points extended by this |
||
65 | // class during system startup. |
||
66 | // |
||
67 | |||
68 | private const string PLUGIN_ASSET_CACHE = "/OpenSim/AssetCache"; |
||
69 | private const string PLUGIN_ASSET_SERVER_CLIENT = "/OpenSim/AssetClient"; |
||
70 | |||
71 | // OpenSim.ini Section name for ESTATES Settings |
||
72 | public const string ESTATE_SECTION_NAME = "Estates"; |
||
73 | |||
74 | /// <summary> |
||
75 | /// Allow all plugin loading to be disabled for tests/debug. |
||
76 | /// </summary> |
||
77 | /// <remarks> |
||
78 | /// true by default |
||
79 | /// </remarks> |
||
80 | public bool EnableInitialPluginLoad { get; set; } |
||
81 | |||
82 | /// <summary> |
||
83 | /// Control whether we attempt to load an estate data service. |
||
84 | /// </summary> |
||
85 | /// <remarks>For tests/debugging</remarks> |
||
86 | public bool LoadEstateDataService { get; set; } |
||
87 | |||
88 | protected string proxyUrl; |
||
89 | protected int proxyOffset = 0; |
||
90 | |||
91 | public string userStatsURI = String.Empty; |
||
92 | public string managedStatsURI = String.Empty; |
||
93 | |||
94 | protected bool m_autoCreateClientStack = true; |
||
95 | |||
96 | /// <value> |
||
97 | /// The file used to load and save prim backup xml if no filename has been specified |
||
98 | /// </value> |
||
99 | protected const string DEFAULT_PRIM_BACKUP_FILENAME = "prim-backup.xml"; |
||
100 | |||
101 | public ConfigSettings ConfigurationSettings |
||
102 | { |
||
103 | get { return m_configSettings; } |
||
104 | set { m_configSettings = value; } |
||
105 | } |
||
106 | |||
107 | protected ConfigSettings m_configSettings; |
||
108 | |||
109 | protected ConfigurationLoader m_configLoader; |
||
110 | |||
111 | public ConsoleCommand CreateAccount = null; |
||
112 | |||
113 | public List<IApplicationPlugin> m_plugins = new List<IApplicationPlugin>(); |
||
114 | |||
115 | /// <value> |
||
116 | /// The config information passed into the OpenSimulator region server. |
||
117 | /// </value> |
||
118 | public OpenSimConfigSource ConfigSource { get; private set; } |
||
119 | |||
120 | public List<IClientNetworkServer> ClientServers |
||
121 | { |
||
122 | get { return m_clientServers; } |
||
123 | } |
||
124 | |||
125 | protected EnvConfigSource m_EnvConfigSource = new EnvConfigSource(); |
||
126 | |||
127 | public EnvConfigSource envConfigSource |
||
128 | { |
||
129 | get { return m_EnvConfigSource; } |
||
130 | } |
||
131 | |||
132 | protected List<IClientNetworkServer> m_clientServers = new List<IClientNetworkServer>(); |
||
133 | |||
134 | public uint HttpServerPort |
||
135 | { |
||
136 | get { return m_httpServerPort; } |
||
137 | } |
||
138 | |||
139 | protected IRegistryCore m_applicationRegistry = new RegistryCore(); |
||
140 | |||
141 | public IRegistryCore ApplicationRegistry |
||
142 | { |
||
143 | get { return m_applicationRegistry; } |
||
144 | } |
||
145 | |||
146 | /// <summary> |
||
147 | /// Constructor. |
||
148 | /// </summary> |
||
149 | /// <param name="configSource"></param> |
||
150 | public OpenSimBase(IConfigSource configSource) : base() |
||
151 | { |
||
152 | EnableInitialPluginLoad = true; |
||
153 | LoadEstateDataService = true; |
||
154 | LoadConfigSettings(configSource); |
||
155 | } |
||
156 | |||
157 | protected virtual void LoadConfigSettings(IConfigSource configSource) |
||
158 | { |
||
159 | m_configLoader = new ConfigurationLoader(); |
||
160 | ConfigSource = m_configLoader.LoadConfigSettings(configSource, envConfigSource, out m_configSettings, out m_networkServersInfo); |
||
161 | Config = ConfigSource.Source; |
||
162 | ReadExtraConfigSettings(); |
||
163 | } |
||
164 | |||
165 | protected virtual void ReadExtraConfigSettings() |
||
166 | { |
||
167 | IConfig networkConfig = Config.Configs["Network"]; |
||
168 | if (networkConfig != null) |
||
169 | { |
||
170 | proxyUrl = networkConfig.GetString("proxy_url", ""); |
||
171 | proxyOffset = Int32.Parse(networkConfig.GetString("proxy_offset", "0")); |
||
172 | } |
||
173 | |||
174 | IConfig startupConfig = Config.Configs["Startup"]; |
||
175 | if (startupConfig != null) |
||
176 | { |
||
177 | Util.LogOverloads = startupConfig.GetBoolean("LogOverloads", true); |
||
178 | } |
||
179 | } |
||
180 | |||
181 | protected virtual void LoadPlugins() |
||
182 | { |
||
183 | IConfig startupConfig = Config.Configs["Startup"]; |
||
184 | string registryLocation = (startupConfig != null) ? startupConfig.GetString("RegistryLocation", String.Empty) : String.Empty; |
||
185 | |||
186 | // The location can also be specified in the environment. If there |
||
187 | // is no location in the configuration, we must call the constructor |
||
188 | // without a location parameter to allow that to happen. |
||
189 | if (registryLocation == String.Empty) |
||
190 | { |
||
191 | using (PluginLoader<IApplicationPlugin> loader = new PluginLoader<IApplicationPlugin>(new ApplicationPluginInitialiser(this))) |
||
192 | { |
||
193 | loader.Load("/OpenSim/Startup"); |
||
194 | m_plugins = loader.Plugins; |
||
195 | } |
||
196 | } |
||
197 | else |
||
198 | { |
||
199 | using (PluginLoader<IApplicationPlugin> loader = new PluginLoader<IApplicationPlugin>(new ApplicationPluginInitialiser(this), registryLocation)) |
||
200 | { |
||
201 | loader.Load("/OpenSim/Startup"); |
||
202 | m_plugins = loader.Plugins; |
||
203 | } |
||
204 | } |
||
205 | } |
||
206 | |||
207 | protected override List<string> GetHelpTopics() |
||
208 | { |
||
209 | List<string> topics = base.GetHelpTopics(); |
||
210 | Scene s = SceneManager.CurrentOrFirstScene; |
||
211 | if (s != null && s.GetCommanders() != null) |
||
212 | topics.AddRange(s.GetCommanders().Keys); |
||
213 | |||
214 | return topics; |
||
215 | } |
||
216 | |||
217 | /// <summary> |
||
218 | /// Performs startup specific to the region server, including initialization of the scene |
||
219 | /// such as loading configuration from disk. |
||
220 | /// </summary> |
||
221 | protected override void StartupSpecific() |
||
222 | { |
||
223 | IConfig startupConfig = Config.Configs["Startup"]; |
||
224 | if (startupConfig != null) |
||
225 | { |
||
226 | string pidFile = startupConfig.GetString("PIDFile", String.Empty); |
||
227 | if (pidFile != String.Empty) |
||
228 | CreatePIDFile(pidFile); |
||
229 | |||
230 | userStatsURI = startupConfig.GetString("Stats_URI", String.Empty); |
||
231 | managedStatsURI = startupConfig.GetString("ManagedStatsRemoteFetchURI", String.Empty); |
||
232 | } |
||
233 | |||
234 | // Load the simulation data service |
||
235 | IConfig simDataConfig = Config.Configs["SimulationDataStore"]; |
||
236 | if (simDataConfig == null) |
||
237 | throw new Exception("Configuration file is missing the [SimulationDataStore] section. Have you copied OpenSim.ini.example to OpenSim.ini to reference config-include/ files?"); |
||
238 | |||
239 | string module = simDataConfig.GetString("LocalServiceModule", String.Empty); |
||
240 | if (String.IsNullOrEmpty(module)) |
||
241 | throw new Exception("Configuration file is missing the LocalServiceModule parameter in the [SimulationDataStore] section."); |
||
242 | |||
243 | m_simulationDataService = ServerUtils.LoadPlugin<ISimulationDataService>(module, new object[] { Config }); |
||
244 | if (m_simulationDataService == null) |
||
245 | throw new Exception( |
||
246 | string.Format( |
||
247 | "Could not load an ISimulationDataService implementation from {0}, as configured in the LocalServiceModule parameter of the [SimulationDataStore] config section.", |
||
248 | module)); |
||
249 | |||
250 | // Load the estate data service |
||
251 | module = Util.GetConfigVarFromSections<string>(Config, "LocalServiceModule", new string[]{"EstateDataStore", "EstateService"}, String.Empty); |
||
252 | if (String.IsNullOrEmpty(module)) |
||
253 | throw new Exception("Configuration file is missing the LocalServiceModule parameter in the [EstateDataStore] or [EstateService] section"); |
||
254 | |||
255 | if (LoadEstateDataService) |
||
256 | { |
||
257 | m_estateDataService = ServerUtils.LoadPlugin<IEstateDataService>(module, new object[] { Config }); |
||
258 | if (m_estateDataService == null) |
||
259 | throw new Exception( |
||
260 | string.Format( |
||
261 | "Could not load an IEstateDataService implementation from {0}, as configured in the LocalServiceModule parameter of the [EstateDataStore] config section.", |
||
262 | module)); |
||
263 | } |
||
264 | |||
265 | base.StartupSpecific(); |
||
266 | |||
267 | if (EnableInitialPluginLoad) |
||
268 | LoadPlugins(); |
||
269 | |||
270 | // We still want to post initalize any plugins even if loading has been disabled since a test may have |
||
271 | // inserted them manually. |
||
272 | foreach (IApplicationPlugin plugin in m_plugins) |
||
273 | plugin.PostInitialise(); |
||
274 | |||
275 | if (m_console != null) |
||
276 | AddPluginCommands(m_console); |
||
277 | } |
||
278 | |||
279 | protected virtual void AddPluginCommands(ICommandConsole console) |
||
280 | { |
||
281 | List<string> topics = GetHelpTopics(); |
||
282 | |||
283 | foreach (string topic in topics) |
||
284 | { |
||
285 | string capitalizedTopic = char.ToUpper(topic[0]) + topic.Substring(1); |
||
286 | |||
287 | // This is a hack to allow the user to enter the help command in upper or lowercase. This will go |
||
288 | // away at some point. |
||
289 | console.Commands.AddCommand(capitalizedTopic, false, "help " + topic, |
||
290 | "help " + capitalizedTopic, |
||
291 | "Get help on plugin command '" + topic + "'", |
||
292 | HandleCommanderHelp); |
||
293 | console.Commands.AddCommand(capitalizedTopic, false, "help " + capitalizedTopic, |
||
294 | "help " + capitalizedTopic, |
||
295 | "Get help on plugin command '" + topic + "'", |
||
296 | HandleCommanderHelp); |
||
297 | |||
298 | ICommander commander = null; |
||
299 | |||
300 | Scene s = SceneManager.CurrentOrFirstScene; |
||
301 | |||
302 | if (s != null && s.GetCommanders() != null) |
||
303 | { |
||
304 | if (s.GetCommanders().ContainsKey(topic)) |
||
305 | commander = s.GetCommanders()[topic]; |
||
306 | } |
||
307 | |||
308 | if (commander == null) |
||
309 | continue; |
||
310 | |||
311 | foreach (string command in commander.Commands.Keys) |
||
312 | { |
||
313 | console.Commands.AddCommand(capitalizedTopic, false, |
||
314 | topic + " " + command, |
||
315 | topic + " " + commander.Commands[command].ShortHelp(), |
||
316 | String.Empty, HandleCommanderCommand); |
||
317 | } |
||
318 | } |
||
319 | } |
||
320 | |||
321 | private void HandleCommanderCommand(string module, string[] cmd) |
||
322 | { |
||
323 | SceneManager.SendCommandToPluginModules(cmd); |
||
324 | } |
||
325 | |||
326 | private void HandleCommanderHelp(string module, string[] cmd) |
||
327 | { |
||
328 | // Only safe for the interactive console, since it won't |
||
329 | // let us come here unless both scene and commander exist |
||
330 | // |
||
331 | ICommander moduleCommander = SceneManager.CurrentOrFirstScene.GetCommander(cmd[1].ToLower()); |
||
332 | if (moduleCommander != null) |
||
333 | m_console.Output(moduleCommander.Help); |
||
334 | } |
||
335 | |||
336 | protected override void Initialize() |
||
337 | { |
||
338 | // Called from base.StartUp() |
||
339 | |||
340 | m_httpServerPort = m_networkServersInfo.HttpListenerPort; |
||
341 | SceneManager.OnRestartSim += HandleRestartRegion; |
||
342 | |||
343 | // Only enable the watchdogs when all regions are ready. Otherwise we get false positives when cpu is |
||
344 | // heavily used during initial startup. |
||
345 | // |
||
346 | // FIXME: It's also possible that region ready status should be flipped during an OAR load since this |
||
347 | // also makes heavy use of the CPU. |
||
348 | SceneManager.OnRegionsReadyStatusChange |
||
349 | += sm => { MemoryWatchdog.Enabled = sm.AllRegionsReady; Watchdog.Enabled = sm.AllRegionsReady; }; |
||
350 | } |
||
351 | |||
352 | /// <summary> |
||
353 | /// Execute the region creation process. This includes setting up scene infrastructure. |
||
354 | /// </summary> |
||
355 | /// <param name="regionInfo"></param> |
||
356 | /// <param name="portadd_flag"></param> |
||
357 | /// <returns></returns> |
||
358 | public List<IClientNetworkServer> CreateRegion(RegionInfo regionInfo, bool portadd_flag, out IScene scene) |
||
359 | { |
||
360 | return CreateRegion(regionInfo, portadd_flag, false, out scene); |
||
361 | } |
||
362 | |||
363 | /// <summary> |
||
364 | /// Execute the region creation process. This includes setting up scene infrastructure. |
||
365 | /// </summary> |
||
366 | /// <param name="regionInfo"></param> |
||
367 | /// <returns></returns> |
||
368 | public List<IClientNetworkServer> CreateRegion(RegionInfo regionInfo, out IScene scene) |
||
369 | { |
||
370 | return CreateRegion(regionInfo, false, true, out scene); |
||
371 | } |
||
372 | |||
373 | /// <summary> |
||
374 | /// Execute the region creation process. This includes setting up scene infrastructure. |
||
375 | /// </summary> |
||
376 | /// <param name="regionInfo"></param> |
||
377 | /// <param name="portadd_flag"></param> |
||
378 | /// <param name="do_post_init"></param> |
||
379 | /// <returns></returns> |
||
380 | public List<IClientNetworkServer> CreateRegion(RegionInfo regionInfo, bool portadd_flag, bool do_post_init, out IScene mscene) |
||
381 | { |
||
382 | int port = regionInfo.InternalEndPoint.Port; |
||
383 | |||
384 | // set initial RegionID to originRegionID in RegionInfo. (it needs for loding prims) |
||
385 | // Commented this out because otherwise regions can't register with |
||
386 | // the grid as there is already another region with the same UUID |
||
387 | // at those coordinates. This is required for the load balancer to work. |
||
388 | // --Mike, 2009.02.25 |
||
389 | //regionInfo.originRegionID = regionInfo.RegionID; |
||
390 | |||
391 | // set initial ServerURI |
||
392 | regionInfo.HttpPort = m_httpServerPort; |
||
393 | regionInfo.ServerURI = "http://" + regionInfo.ExternalHostName + ":" + regionInfo.HttpPort.ToString() + "/"; |
||
394 | |||
395 | regionInfo.osSecret = m_osSecret; |
||
396 | |||
397 | if ((proxyUrl.Length > 0) && (portadd_flag)) |
||
398 | { |
||
399 | // set proxy url to RegionInfo |
||
400 | regionInfo.proxyUrl = proxyUrl; |
||
401 | regionInfo.ProxyOffset = proxyOffset; |
||
402 | Util.XmlRpcCommand(proxyUrl, "AddPort", port, port + proxyOffset, regionInfo.ExternalHostName); |
||
403 | } |
||
404 | |||
405 | List<IClientNetworkServer> clientServers; |
||
406 | Scene scene = SetupScene(regionInfo, proxyOffset, Config, out clientServers); |
||
407 | |||
408 | m_log.Info("[MODULES]: Loading Region's modules (old style)"); |
||
409 | |||
410 | // Use this in the future, the line above will be deprecated soon |
||
411 | m_log.Info("[REGIONMODULES]: Loading Region's modules (new style)"); |
||
412 | IRegionModulesController controller; |
||
413 | if (ApplicationRegistry.TryGet(out controller)) |
||
414 | { |
||
415 | controller.AddRegionToModules(scene); |
||
416 | } |
||
417 | else m_log.Error("[REGIONMODULES]: The new RegionModulesController is missing..."); |
||
418 | |||
419 | scene.SetModuleInterfaces(); |
||
420 | |||
421 | while (regionInfo.EstateSettings.EstateOwner == UUID.Zero && MainConsole.Instance != null) |
||
422 | SetUpEstateOwner(scene); |
||
423 | |||
424 | // Prims have to be loaded after module configuration since some modules may be invoked during the load |
||
425 | scene.LoadPrimsFromStorage(regionInfo.originRegionID); |
||
426 | |||
427 | // TODO : Try setting resource for region xstats here on scene |
||
428 | MainServer.Instance.AddStreamHandler(new RegionStatsHandler(regionInfo)); |
||
429 | |||
430 | scene.loadAllLandObjectsFromStorage(regionInfo.originRegionID); |
||
431 | scene.EventManager.TriggerParcelPrimCountUpdate(); |
||
432 | |||
433 | try |
||
434 | { |
||
435 | scene.RegisterRegionWithGrid(); |
||
436 | } |
||
437 | catch (Exception e) |
||
438 | { |
||
439 | m_log.ErrorFormat( |
||
440 | "[STARTUP]: Registration of region with grid failed, aborting startup due to {0} {1}", |
||
441 | e.Message, e.StackTrace); |
||
442 | |||
443 | // Carrying on now causes a lot of confusion down the |
||
444 | // line - we need to get the user's attention |
||
445 | Environment.Exit(1); |
||
446 | } |
||
447 | |||
448 | // We need to do this after we've initialized the |
||
449 | // scripting engines. |
||
450 | scene.CreateScriptInstances(); |
||
451 | |||
452 | SceneManager.Add(scene); |
||
453 | |||
454 | if (m_autoCreateClientStack) |
||
455 | { |
||
456 | foreach (IClientNetworkServer clientserver in clientServers) |
||
457 | { |
||
458 | m_clientServers.Add(clientserver); |
||
459 | clientserver.Start(); |
||
460 | } |
||
461 | } |
||
462 | |||
463 | scene.EventManager.OnShutdown += delegate() { ShutdownRegion(scene); }; |
||
464 | |||
465 | mscene = scene; |
||
466 | |||
467 | return clientServers; |
||
468 | } |
||
469 | |||
470 | /// <summary> |
||
471 | /// Try to set up the estate owner for the given scene. |
||
472 | /// </summary> |
||
473 | /// <remarks> |
||
474 | /// The involves asking the user for information about the user on the console. If the user does not already |
||
475 | /// exist then it is created. |
||
476 | /// </remarks> |
||
477 | /// <param name="scene"></param> |
||
478 | private void SetUpEstateOwner(Scene scene) |
||
479 | { |
||
480 | RegionInfo regionInfo = scene.RegionInfo; |
||
481 | |||
482 | string estateOwnerFirstName = null; |
||
483 | string estateOwnerLastName = null; |
||
484 | string estateOwnerEMail = null; |
||
485 | string estateOwnerPassword = null; |
||
486 | string rawEstateOwnerUuid = null; |
||
487 | |||
488 | if (Config.Configs[ESTATE_SECTION_NAME] != null) |
||
489 | { |
||
490 | string defaultEstateOwnerName |
||
491 | = Config.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerName", "").Trim(); |
||
492 | string[] ownerNames = defaultEstateOwnerName.Split(' '); |
||
493 | |||
494 | if (ownerNames.Length >= 2) |
||
495 | { |
||
496 | estateOwnerFirstName = ownerNames[0]; |
||
497 | estateOwnerLastName = ownerNames[1]; |
||
498 | } |
||
499 | |||
500 | // Info to be used only on Standalone Mode |
||
501 | rawEstateOwnerUuid = Config.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerUUID", null); |
||
502 | estateOwnerEMail = Config.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerEMail", null); |
||
503 | estateOwnerPassword = Config.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateOwnerPassword", null); |
||
504 | } |
||
505 | |||
506 | MainConsole.Instance.OutputFormat("Estate {0} has no owner set.", regionInfo.EstateSettings.EstateName); |
||
507 | List<char> excluded = new List<char>(new char[1]{' '}); |
||
508 | |||
509 | |||
510 | if (estateOwnerFirstName == null || estateOwnerLastName == null) |
||
511 | { |
||
512 | estateOwnerFirstName = MainConsole.Instance.CmdPrompt("Estate owner first name", "Test", excluded); |
||
513 | estateOwnerLastName = MainConsole.Instance.CmdPrompt("Estate owner last name", "User", excluded); |
||
514 | } |
||
515 | |||
516 | UserAccount account |
||
517 | = scene.UserAccountService.GetUserAccount(regionInfo.ScopeID, estateOwnerFirstName, estateOwnerLastName); |
||
518 | |||
519 | if (account == null) |
||
520 | { |
||
521 | |||
522 | // XXX: The LocalUserAccountServicesConnector is currently registering its inner service rather than |
||
523 | // itself! |
||
524 | // if (scene.UserAccountService is LocalUserAccountServicesConnector) |
||
525 | // { |
||
526 | // IUserAccountService innerUas |
||
527 | // = ((LocalUserAccountServicesConnector)scene.UserAccountService).UserAccountService; |
||
528 | // |
||
529 | // m_log.DebugFormat("B {0}", innerUas.GetType()); |
||
530 | // |
||
531 | // if (innerUas is UserAccountService) |
||
532 | // { |
||
533 | |||
534 | if (scene.UserAccountService is UserAccountService) |
||
535 | { |
||
536 | if (estateOwnerPassword == null) |
||
537 | estateOwnerPassword = MainConsole.Instance.PasswdPrompt("Password"); |
||
538 | |||
539 | if (estateOwnerEMail == null) |
||
540 | estateOwnerEMail = MainConsole.Instance.CmdPrompt("Email"); |
||
541 | |||
542 | if (rawEstateOwnerUuid == null) |
||
543 | rawEstateOwnerUuid = MainConsole.Instance.CmdPrompt("User ID", UUID.Random().ToString()); |
||
544 | |||
545 | UUID estateOwnerUuid = UUID.Zero; |
||
546 | if (!UUID.TryParse(rawEstateOwnerUuid, out estateOwnerUuid)) |
||
547 | { |
||
548 | m_log.ErrorFormat("[OPENSIM]: ID {0} is not a valid UUID", rawEstateOwnerUuid); |
||
549 | return; |
||
550 | } |
||
551 | |||
552 | // If we've been given a zero uuid then this signals that we should use a random user id |
||
553 | if (estateOwnerUuid == UUID.Zero) |
||
554 | estateOwnerUuid = UUID.Random(); |
||
555 | |||
556 | account |
||
557 | = ((UserAccountService)scene.UserAccountService).CreateUser( |
||
558 | regionInfo.ScopeID, |
||
559 | estateOwnerUuid, |
||
560 | estateOwnerFirstName, |
||
561 | estateOwnerLastName, |
||
562 | estateOwnerPassword, |
||
563 | estateOwnerEMail); |
||
564 | } |
||
565 | } |
||
566 | |||
567 | if (account == null) |
||
568 | { |
||
569 | m_log.ErrorFormat( |
||
570 | "[OPENSIM]: Unable to store account. If this simulator is connected to a grid, you must create the estate owner account first at the grid level."); |
||
571 | } |
||
572 | else |
||
573 | { |
||
574 | regionInfo.EstateSettings.EstateOwner = account.PrincipalID; |
||
575 | m_estateDataService.StoreEstateSettings(regionInfo.EstateSettings); |
||
576 | } |
||
577 | } |
||
578 | |||
579 | private void ShutdownRegion(Scene scene) |
||
580 | { |
||
581 | m_log.DebugFormat("[SHUTDOWN]: Shutting down region {0}", scene.RegionInfo.RegionName); |
||
582 | IRegionModulesController controller; |
||
583 | if (ApplicationRegistry.TryGet<IRegionModulesController>(out controller)) |
||
584 | { |
||
585 | controller.RemoveRegionFromModules(scene); |
||
586 | } |
||
587 | } |
||
588 | |||
589 | public void RemoveRegion(Scene scene, bool cleanup) |
||
590 | { |
||
591 | // only need to check this if we are not at the |
||
592 | // root level |
||
593 | if ((SceneManager.CurrentScene != null) && |
||
594 | (SceneManager.CurrentScene.RegionInfo.RegionID == scene.RegionInfo.RegionID)) |
||
595 | { |
||
596 | SceneManager.TrySetCurrentScene(".."); |
||
597 | } |
||
598 | |||
599 | scene.DeleteAllSceneObjects(); |
||
600 | SceneManager.CloseScene(scene); |
||
601 | ShutdownClientServer(scene.RegionInfo); |
||
602 | |||
603 | if (!cleanup) |
||
604 | return; |
||
605 | |||
606 | if (!String.IsNullOrEmpty(scene.RegionInfo.RegionFile)) |
||
607 | { |
||
608 | if (scene.RegionInfo.RegionFile.ToLower().EndsWith(".xml")) |
||
609 | { |
||
610 | File.Delete(scene.RegionInfo.RegionFile); |
||
611 | m_log.InfoFormat("[OPENSIM]: deleting region file \"{0}\"", scene.RegionInfo.RegionFile); |
||
612 | } |
||
613 | if (scene.RegionInfo.RegionFile.ToLower().EndsWith(".ini")) |
||
614 | { |
||
615 | try |
||
616 | { |
||
617 | IniConfigSource source = new IniConfigSource(scene.RegionInfo.RegionFile); |
||
618 | if (source.Configs[scene.RegionInfo.RegionName] != null) |
||
619 | { |
||
620 | source.Configs.Remove(scene.RegionInfo.RegionName); |
||
621 | |||
622 | if (source.Configs.Count == 0) |
||
623 | { |
||
624 | File.Delete(scene.RegionInfo.RegionFile); |
||
625 | } |
||
626 | else |
||
627 | { |
||
628 | source.Save(scene.RegionInfo.RegionFile); |
||
629 | } |
||
630 | } |
||
631 | } |
||
632 | catch (Exception) |
||
633 | { |
||
634 | } |
||
635 | } |
||
636 | } |
||
637 | } |
||
638 | |||
639 | public void RemoveRegion(string name, bool cleanUp) |
||
640 | { |
||
641 | Scene target; |
||
642 | if (SceneManager.TryGetScene(name, out target)) |
||
643 | RemoveRegion(target, cleanUp); |
||
644 | } |
||
645 | |||
646 | /// <summary> |
||
647 | /// Remove a region from the simulator without deleting it permanently. |
||
648 | /// </summary> |
||
649 | /// <param name="scene"></param> |
||
650 | /// <returns></returns> |
||
651 | public void CloseRegion(Scene scene) |
||
652 | { |
||
653 | // only need to check this if we are not at the |
||
654 | // root level |
||
655 | if ((SceneManager.CurrentScene != null) && |
||
656 | (SceneManager.CurrentScene.RegionInfo.RegionID == scene.RegionInfo.RegionID)) |
||
657 | { |
||
658 | SceneManager.TrySetCurrentScene(".."); |
||
659 | } |
||
660 | |||
661 | SceneManager.CloseScene(scene); |
||
662 | ShutdownClientServer(scene.RegionInfo); |
||
663 | } |
||
664 | |||
665 | /// <summary> |
||
666 | /// Remove a region from the simulator without deleting it permanently. |
||
667 | /// </summary> |
||
668 | /// <param name="name"></param> |
||
669 | /// <returns></returns> |
||
670 | public void CloseRegion(string name) |
||
671 | { |
||
672 | Scene target; |
||
673 | if (SceneManager.TryGetScene(name, out target)) |
||
674 | CloseRegion(target); |
||
675 | } |
||
676 | |||
677 | /// <summary> |
||
678 | /// Create a scene and its initial base structures. |
||
679 | /// </summary> |
||
680 | /// <param name="regionInfo"></param> |
||
681 | /// <param name="clientServer"> </param> |
||
682 | /// <returns></returns> |
||
683 | protected Scene SetupScene(RegionInfo regionInfo, out List<IClientNetworkServer> clientServer) |
||
684 | { |
||
685 | return SetupScene(regionInfo, 0, null, out clientServer); |
||
686 | } |
||
687 | |||
688 | /// <summary> |
||
689 | /// Create a scene and its initial base structures. |
||
690 | /// </summary> |
||
691 | /// <param name="regionInfo"></param> |
||
692 | /// <param name="proxyOffset"></param> |
||
693 | /// <param name="configSource"></param> |
||
694 | /// <param name="clientServer"> </param> |
||
695 | /// <returns></returns> |
||
696 | protected Scene SetupScene( |
||
697 | RegionInfo regionInfo, int proxyOffset, IConfigSource configSource, out List<IClientNetworkServer> clientServer) |
||
698 | { |
||
699 | List<IClientNetworkServer> clientNetworkServers = null; |
||
700 | |||
701 | AgentCircuitManager circuitManager = new AgentCircuitManager(); |
||
702 | IPAddress listenIP = regionInfo.InternalEndPoint.Address; |
||
703 | //if (!IPAddress.TryParse(regionInfo.InternalEndPoint, out listenIP)) |
||
704 | // listenIP = IPAddress.Parse("0.0.0.0"); |
||
705 | |||
706 | uint port = (uint) regionInfo.InternalEndPoint.Port; |
||
707 | |||
708 | if (m_autoCreateClientStack) |
||
709 | { |
||
710 | clientNetworkServers = m_clientStackManager.CreateServers( |
||
711 | listenIP, ref port, proxyOffset, regionInfo.m_allow_alternate_ports, configSource, |
||
712 | circuitManager); |
||
713 | } |
||
714 | else |
||
715 | { |
||
716 | clientServer = null; |
||
717 | } |
||
718 | |||
719 | regionInfo.InternalEndPoint.Port = (int) port; |
||
720 | |||
721 | Scene scene = CreateScene(regionInfo, m_simulationDataService, m_estateDataService, circuitManager); |
||
722 | |||
723 | if (m_autoCreateClientStack) |
||
724 | { |
||
725 | foreach (IClientNetworkServer clientnetserver in clientNetworkServers) |
||
726 | { |
||
727 | clientnetserver.AddScene(scene); |
||
728 | } |
||
729 | } |
||
730 | clientServer = clientNetworkServers; |
||
731 | scene.LoadWorldMap(); |
||
732 | |||
733 | scene.PhysicsScene.RequestAssetMethod = scene.PhysicsRequestAsset; |
||
734 | scene.PhysicsScene.SetTerrain(scene.Heightmap.GetFloatsSerialised()); |
||
735 | scene.PhysicsScene.SetWaterLevel((float) regionInfo.RegionSettings.WaterHeight); |
||
736 | |||
737 | return scene; |
||
738 | } |
||
739 | |||
740 | protected override ClientStackManager CreateClientStackManager() |
||
741 | { |
||
742 | return new ClientStackManager(m_configSettings.ClientstackDll); |
||
743 | } |
||
744 | |||
745 | protected override Scene CreateScene(RegionInfo regionInfo, ISimulationDataService simDataService, |
||
746 | IEstateDataService estateDataService, AgentCircuitManager circuitManager) |
||
747 | { |
||
748 | Vector3 regionExtent = new Vector3(regionInfo.RegionSizeX, regionInfo.RegionSizeY, regionInfo.RegionSizeZ); |
||
749 | PhysicsScene physicsScene = GetPhysicsScene(regionInfo.RegionName, regionExtent); |
||
750 | |||
751 | SceneCommunicationService sceneGridService = new SceneCommunicationService(); |
||
752 | |||
753 | return new Scene( |
||
754 | regionInfo, circuitManager, physicsScene, sceneGridService, |
||
755 | simDataService, estateDataService, |
||
756 | Config, m_version); |
||
757 | } |
||
758 | |||
759 | protected void ShutdownClientServer(RegionInfo whichRegion) |
||
760 | { |
||
761 | // Close and remove the clientserver for a region |
||
762 | bool foundClientServer = false; |
||
763 | int clientServerElement = 0; |
||
764 | Location location = new Location(whichRegion.RegionHandle); |
||
765 | |||
766 | for (int i = 0; i < m_clientServers.Count; i++) |
||
767 | { |
||
768 | if (m_clientServers[i].HandlesRegion(location)) |
||
769 | { |
||
770 | clientServerElement = i; |
||
771 | foundClientServer = true; |
||
772 | break; |
||
773 | } |
||
774 | } |
||
775 | |||
776 | if (foundClientServer) |
||
777 | { |
||
778 | m_clientServers[clientServerElement].Stop(); |
||
779 | m_clientServers.RemoveAt(clientServerElement); |
||
780 | } |
||
781 | } |
||
782 | |||
783 | protected virtual void HandleRestartRegion(RegionInfo whichRegion) |
||
784 | { |
||
785 | m_log.InfoFormat( |
||
786 | "[OPENSIM]: Got restart signal from SceneManager for region {0} ({1},{2})", |
||
787 | whichRegion.RegionName, whichRegion.RegionLocX, whichRegion.RegionLocY); |
||
788 | |||
789 | ShutdownClientServer(whichRegion); |
||
790 | IScene scene; |
||
791 | CreateRegion(whichRegion, true, out scene); |
||
792 | scene.Start(); |
||
793 | } |
||
794 | |||
795 | # region Setup methods |
||
796 | |||
797 | protected override PhysicsScene GetPhysicsScene(string osSceneIdentifier, Vector3 regionExtent) |
||
798 | { |
||
799 | return GetPhysicsScene( |
||
800 | m_configSettings.PhysicsEngine, m_configSettings.MeshEngineName, Config, osSceneIdentifier, regionExtent); |
||
801 | } |
||
802 | |||
803 | /// <summary> |
||
804 | /// Handler to supply the current status of this sim |
||
805 | /// </summary> |
||
806 | /// <remarks> |
||
807 | /// Currently this is always OK if the simulator is still listening for connections on its HTTP service |
||
808 | /// </remarks> |
||
809 | public class SimStatusHandler : BaseStreamHandler |
||
810 | { |
||
811 | public SimStatusHandler() : base("GET", "/simstatus", "SimStatus", "Simulator Status") {} |
||
812 | |||
813 | protected override byte[] ProcessRequest(string path, Stream request, |
||
814 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
||
815 | { |
||
816 | return Util.UTF8.GetBytes("OK"); |
||
817 | } |
||
818 | |||
819 | public override string ContentType |
||
820 | { |
||
821 | get { return "text/plain"; } |
||
822 | } |
||
823 | } |
||
824 | |||
825 | /// <summary> |
||
826 | /// Handler to supply the current extended status of this sim |
||
827 | /// Sends the statistical data in a json serialization |
||
828 | /// </summary> |
||
829 | public class XSimStatusHandler : BaseStreamHandler |
||
830 | { |
||
831 | OpenSimBase m_opensim; |
||
832 | |||
833 | public XSimStatusHandler(OpenSimBase sim) |
||
834 | : base("GET", "/" + Util.SHA1Hash(sim.osSecret), "XSimStatus", "Simulator XStatus") |
||
835 | { |
||
836 | m_opensim = sim; |
||
837 | } |
||
838 | |||
839 | protected override byte[] ProcessRequest(string path, Stream request, |
||
840 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
||
841 | { |
||
842 | return Util.UTF8.GetBytes(m_opensim.StatReport(httpRequest)); |
||
843 | } |
||
844 | |||
845 | public override string ContentType |
||
846 | { |
||
847 | get { return "text/plain"; } |
||
848 | } |
||
849 | } |
||
850 | |||
851 | /// <summary> |
||
852 | /// Handler to supply the current extended status of this sim to a user configured URI |
||
853 | /// Sends the statistical data in a json serialization |
||
854 | /// If the request contains a key, "callback" the response will be wrappend in the |
||
855 | /// associated value for jsonp used with ajax/javascript |
||
856 | /// </summary> |
||
857 | protected class UXSimStatusHandler : BaseStreamHandler |
||
858 | { |
||
859 | OpenSimBase m_opensim; |
||
860 | |||
861 | public UXSimStatusHandler(OpenSimBase sim) |
||
862 | : base("GET", "/" + sim.userStatsURI, "UXSimStatus", "Simulator UXStatus") |
||
863 | { |
||
864 | m_opensim = sim; |
||
865 | } |
||
866 | |||
867 | protected override byte[] ProcessRequest(string path, Stream request, |
||
868 | IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) |
||
869 | { |
||
870 | return Util.UTF8.GetBytes(m_opensim.StatReport(httpRequest)); |
||
871 | } |
||
872 | |||
873 | public override string ContentType |
||
874 | { |
||
875 | get { return "text/plain"; } |
||
876 | } |
||
877 | } |
||
878 | |||
879 | #endregion |
||
880 | |||
881 | /// <summary> |
||
882 | /// Performs any last-minute sanity checking and shuts down the region server |
||
883 | /// </summary> |
||
884 | protected override void ShutdownSpecific() |
||
885 | { |
||
886 | if (proxyUrl.Length > 0) |
||
887 | { |
||
888 | Util.XmlRpcCommand(proxyUrl, "Stop"); |
||
889 | } |
||
890 | |||
891 | m_log.Info("[SHUTDOWN]: Closing all threads"); |
||
892 | m_log.Info("[SHUTDOWN]: Killing listener thread"); |
||
893 | m_log.Info("[SHUTDOWN]: Killing clients"); |
||
894 | m_log.Info("[SHUTDOWN]: Closing console and terminating"); |
||
895 | |||
896 | try |
||
897 | { |
||
898 | SceneManager.Close(); |
||
899 | |||
900 | foreach (IApplicationPlugin plugin in m_plugins) |
||
901 | plugin.Dispose(); |
||
902 | } |
||
903 | catch (Exception e) |
||
904 | { |
||
905 | m_log.Error("[SHUTDOWN]: Ignoring failure during shutdown - ", e); |
||
906 | } |
||
907 | |||
908 | base.ShutdownSpecific(); |
||
909 | } |
||
910 | |||
911 | /// <summary> |
||
912 | /// Get the start time and up time of Region server |
||
913 | /// </summary> |
||
914 | /// <param name="starttime">The first out parameter describing when the Region server started</param> |
||
915 | /// <param name="uptime">The second out parameter describing how long the Region server has run</param> |
||
916 | public void GetRunTime(out string starttime, out string uptime) |
||
917 | { |
||
918 | starttime = m_startuptime.ToString(); |
||
919 | uptime = (DateTime.Now - m_startuptime).ToString(); |
||
920 | } |
||
921 | |||
922 | /// <summary> |
||
923 | /// Get the number of the avatars in the Region server |
||
924 | /// </summary> |
||
925 | /// <param name="usernum">The first out parameter describing the number of all the avatars in the Region server</param> |
||
926 | public void GetAvatarNumber(out int usernum) |
||
927 | { |
||
928 | usernum = SceneManager.GetCurrentSceneAvatars().Count; |
||
929 | } |
||
930 | |||
931 | /// <summary> |
||
932 | /// Get the number of regions |
||
933 | /// </summary> |
||
934 | /// <param name="regionnum">The first out parameter describing the number of regions</param> |
||
935 | public void GetRegionNumber(out int regionnum) |
||
936 | { |
||
937 | regionnum = SceneManager.Scenes.Count; |
||
938 | } |
||
939 | |||
940 | /// <summary> |
||
941 | /// Create an estate with an initial region. |
||
942 | /// </summary> |
||
943 | /// <remarks> |
||
944 | /// This method doesn't allow an estate to be created with the same name as existing estates. |
||
945 | /// </remarks> |
||
946 | /// <param name="regInfo"></param> |
||
947 | /// <param name="estatesByName">A list of estate names that already exist.</param> |
||
948 | /// <param name="estateName">Estate name to create if already known</param> |
||
949 | /// <returns>true if the estate was created, false otherwise</returns> |
||
950 | public bool CreateEstate(RegionInfo regInfo, Dictionary<string, EstateSettings> estatesByName, string estateName) |
||
951 | { |
||
952 | // Create a new estate |
||
953 | regInfo.EstateSettings = EstateDataService.LoadEstateSettings(regInfo.RegionID, true); |
||
954 | |||
955 | string newName; |
||
956 | if (!string.IsNullOrEmpty(estateName)) |
||
957 | newName = estateName; |
||
958 | else |
||
959 | newName = MainConsole.Instance.CmdPrompt("New estate name", regInfo.EstateSettings.EstateName); |
||
960 | |||
961 | if (estatesByName.ContainsKey(newName)) |
||
962 | { |
||
963 | MainConsole.Instance.OutputFormat("An estate named {0} already exists. Please try again.", newName); |
||
964 | return false; |
||
965 | } |
||
966 | |||
967 | regInfo.EstateSettings.EstateName = newName; |
||
968 | |||
969 | // FIXME: Later on, the scene constructor will reload the estate settings no matter what. |
||
970 | // Therefore, we need to do an initial save here otherwise the new estate name will be reset |
||
971 | // back to the default. The reloading of estate settings by scene could be eliminated if it |
||
972 | // knows that the passed in settings in RegionInfo are already valid. Also, it might be |
||
973 | // possible to eliminate some additional later saves made by callers of this method. |
||
974 | EstateDataService.StoreEstateSettings(regInfo.EstateSettings); |
||
975 | |||
976 | return true; |
||
977 | } |
||
978 | |||
979 | /// <summary> |
||
980 | /// Load the estate information for the provided RegionInfo object. |
||
981 | /// </summary> |
||
982 | /// <param name="regInfo"></param> |
||
983 | public bool PopulateRegionEstateInfo(RegionInfo regInfo) |
||
984 | { |
||
985 | if (EstateDataService != null) |
||
986 | regInfo.EstateSettings = EstateDataService.LoadEstateSettings(regInfo.RegionID, false); |
||
987 | |||
988 | if (regInfo.EstateSettings.EstateID != 0) |
||
989 | return false; // estate info in the database did not change |
||
990 | |||
991 | m_log.WarnFormat("[ESTATE] Region {0} is not part of an estate.", regInfo.RegionName); |
||
992 | |||
993 | List<EstateSettings> estates = EstateDataService.LoadEstateSettingsAll(); |
||
994 | Dictionary<string, EstateSettings> estatesByName = new Dictionary<string, EstateSettings>(); |
||
995 | |||
996 | foreach (EstateSettings estate in estates) |
||
997 | estatesByName[estate.EstateName] = estate; |
||
998 | |||
999 | string defaultEstateName = null; |
||
1000 | |||
1001 | if (Config.Configs[ESTATE_SECTION_NAME] != null) |
||
1002 | { |
||
1003 | defaultEstateName = Config.Configs[ESTATE_SECTION_NAME].GetString("DefaultEstateName", null); |
||
1004 | |||
1005 | if (defaultEstateName != null) |
||
1006 | { |
||
1007 | EstateSettings defaultEstate; |
||
1008 | bool defaultEstateJoined = false; |
||
1009 | |||
1010 | if (estatesByName.ContainsKey(defaultEstateName)) |
||
1011 | { |
||
1012 | defaultEstate = estatesByName[defaultEstateName]; |
||
1013 | |||
1014 | if (EstateDataService.LinkRegion(regInfo.RegionID, (int)defaultEstate.EstateID)) |
||
1015 | defaultEstateJoined = true; |
||
1016 | } |
||
1017 | else |
||
1018 | { |
||
1019 | if (CreateEstate(regInfo, estatesByName, defaultEstateName)) |
||
1020 | defaultEstateJoined = true; |
||
1021 | } |
||
1022 | |||
1023 | if (defaultEstateJoined) |
||
1024 | return true; // need to update the database |
||
1025 | else |
||
1026 | m_log.ErrorFormat( |
||
1027 | "[OPENSIM BASE]: Joining default estate {0} failed", defaultEstateName); |
||
1028 | } |
||
1029 | } |
||
1030 | |||
1031 | // If we have no default estate or creation of the default estate failed then ask the user. |
||
1032 | while (true) |
||
1033 | { |
||
1034 | if (estates.Count == 0) |
||
1035 | { |
||
1036 | m_log.Info("[ESTATE]: No existing estates found. You must create a new one."); |
||
1037 | |||
1038 | if (CreateEstate(regInfo, estatesByName, null)) |
||
1039 | break; |
||
1040 | else |
||
1041 | continue; |
||
1042 | } |
||
1043 | else |
||
1044 | { |
||
1045 | string response |
||
1046 | = MainConsole.Instance.CmdPrompt( |
||
1047 | string.Format( |
||
1048 | "Do you wish to join region {0} to an existing estate (yes/no)?", regInfo.RegionName), |
||
1049 | "yes", |
||
1050 | new List<string>() { "yes", "no" }); |
||
1051 | |||
1052 | if (response == "no") |
||
1053 | { |
||
1054 | if (CreateEstate(regInfo, estatesByName, null)) |
||
1055 | break; |
||
1056 | else |
||
1057 | continue; |
||
1058 | } |
||
1059 | else |
||
1060 | { |
||
1061 | string[] estateNames = estatesByName.Keys.ToArray(); |
||
1062 | response |
||
1063 | = MainConsole.Instance.CmdPrompt( |
||
1064 | string.Format( |
||
1065 | "Name of estate to join. Existing estate names are ({0})", |
||
1066 | string.Join(", ", estateNames)), |
||
1067 | estateNames[0]); |
||
1068 | |||
1069 | List<int> estateIDs = EstateDataService.GetEstates(response); |
||
1070 | if (estateIDs.Count < 1) |
||
1071 | { |
||
1072 | MainConsole.Instance.Output("The name you have entered matches no known estate. Please try again."); |
||
1073 | continue; |
||
1074 | } |
||
1075 | |||
1076 | int estateID = estateIDs[0]; |
||
1077 | |||
1078 | regInfo.EstateSettings = EstateDataService.LoadEstateSettings(estateID); |
||
1079 | |||
1080 | if (EstateDataService.LinkRegion(regInfo.RegionID, estateID)) |
||
1081 | break; |
||
1082 | |||
1083 | MainConsole.Instance.Output("Joining the estate failed. Please try again."); |
||
1084 | } |
||
1085 | } |
||
1086 | } |
||
1087 | |||
1088 | return true; // need to update the database |
||
1089 | } |
||
1090 | } |
||
1091 | |||
1092 | public class OpenSimConfigSource |
||
1093 | { |
||
1094 | public IConfigSource Source; |
||
1095 | } |
||
1096 | } |