opensim-development – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 eva 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.Reflection;
31 using log4net;
32 using Mono.Addins;
33 using Nini.Config;
34 using OpenMetaverse;
35 using OpenSim.Framework;
36 using OpenSim.Region.Framework.Interfaces;
37 using OpenSim.Region.Framework.Scenes;
38  
39 namespace OpenSim.Region.CoreModules
40 {
41 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SunModule")]
42 public class SunModule : ISunModule
43 {
44 /// <summary>
45 /// Note: Sun Hour can be a little deceaving. Although it's based on a 24 hour clock
46 /// it is not based on ~06:00 == Sun Rise. Rather it is based on 00:00 being sun-rise.
47 /// </summary>
48  
49 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50  
51 //
52 // Global Constants used to determine where in the sky the sun is
53 //
54 private const double m_SeasonalTilt = 0.03 * Math.PI; // A daily shift of approximately 1.7188 degrees
55 private const double m_AverageTilt = -0.25 * Math.PI; // A 45 degree tilt
56 private const double m_SunCycle = 2.0D * Math.PI; // A perfect circle measured in radians
57 private const double m_SeasonalCycle = 2.0D * Math.PI; // Ditto
58  
59 //
60 // Per Region Values
61 //
62  
63 private bool ready = false;
64  
65 // This solves a chick before the egg problem
66 // the local SunFixedHour and SunFixed variables MUST be updated
67 // at least once with the proper Region Settings before we start
68 // updating those region settings in GenSunPos()
69 private bool receivedEstateToolsSunUpdate = false;
70  
71 // Sun's position information is updated and sent to clients every m_UpdateInterval frames
72 private int m_UpdateInterval = 0;
73  
74 // Number of real time hours per virtual day
75 private double m_DayLengthHours = 0;
76  
77 // Number of virtual days to a virtual year
78 private int m_YearLengthDays = 0;
79  
80 // Ratio of Daylight hours to Night time hours. This is accomplished by shifting the
81 // sun's orbit above the horizon
82 private double m_HorizonShift = 0;
83  
84 // Used to scale current and positional time to adjust length of an hour during day vs night.
85 private double m_DayTimeSunHourScale;
86  
87 // private double m_longitude = 0;
88 // private double m_latitude = 0;
89 // Configurable defaults Defaults close to SL
90 private int d_frame_mod = 100; // Every 10 seconds (actually less)
91 private double d_day_length = 4; // A VW day is 4 RW hours long
92 private int d_year_length = 60; // There are 60 VW days in a VW year
93 private double d_day_night = 0.5; // axis offset: Default Hoizon shift to try and closely match the sun model in LL Viewer
94 private double d_DayTimeSunHourScale = 0.5; // Day/Night hours are equal
95  
96  
97 // private double d_longitude = -73.53;
98 // private double d_latitude = 41.29;
99  
100 // Frame counter
101 private uint m_frame = 0;
102  
103 // Cached Scene reference
104 private Scene m_scene = null;
105  
106 // Calculated Once in the lifetime of a region
107 private long TicksToEpoch; // Elapsed time for 1/1/1970
108 private uint SecondsPerSunCycle; // Length of a virtual day in RW seconds
109 private uint SecondsPerYear; // Length of a virtual year in RW seconds
110 private double SunSpeed; // Rate of passage in radians/second
111 private double SeasonSpeed; // Rate of change for seasonal effects
112 // private double HoursToRadians; // Rate of change for seasonal effects
113 private long TicksUTCOffset = 0; // seconds offset from UTC
114 // Calculated every update
115 private float OrbitalPosition; // Orbital placement at a point in time
116 private double HorizonShift; // Axis offset to skew day and night
117 private double TotalDistanceTravelled; // Distance since beginning of time (in radians)
118 private double SeasonalOffset; // Seaonal variation of tilt
119 private float Magnitude; // Normal tilt
120 // private double VWTimeRatio; // VW time as a ratio of real time
121  
122 // Working values
123 private Vector3 Position = Vector3.Zero;
124 private Vector3 Velocity = Vector3.Zero;
125 private Quaternion Tilt = new Quaternion(1.0f, 0.0f, 0.0f, 0.0f);
126  
127 // Used to fix the sun in the sky so it doesn't move based on current time
128 private bool m_SunFixed = false;
129 private float m_SunFixedHour = 0f;
130  
131 private const int TICKS_PER_SECOND = 10000000;
132  
133 private ulong m_CurrentTimeOffset = 0;
134  
135 // Current time in elapsed seconds since Jan 1st 1970
136 private ulong CurrentTime
137 {
138 get
139 {
140 ulong ctime = (ulong)(((DateTime.Now.Ticks) - TicksToEpoch + TicksUTCOffset) / TICKS_PER_SECOND);
141 return ctime + m_CurrentTimeOffset;
142 }
143 }
144  
145 // Time in seconds since UTC to use to calculate sun position.
146 ulong PosTime = 0;
147  
148 /// <summary>
149 /// Calculate the sun's orbital position and its velocity.
150 /// </summary>
151 private void GenSunPos()
152 {
153 // Time in seconds since UTC to use to calculate sun position.
154 PosTime = CurrentTime;
155  
156 if (m_SunFixed)
157 {
158 // SunFixedHour represents the "hour of day" we would like
159 // It's represented in 24hr time, with 0 hour being sun-rise
160 // Because our day length is probably not 24hrs {LL is 6} we need to do a bit of math
161  
162 // Determine the current "day" from current time, so we can use "today"
163 // to determine Seasonal Tilt and what'not
164  
165 // Integer math rounded is on purpose to drop fractional day, determines number
166 // of virtual days since Epoch
167 PosTime = CurrentTime / SecondsPerSunCycle;
168  
169 // Since we want number of seconds since Epoch, multiply back up
170 PosTime *= SecondsPerSunCycle;
171  
172 // Then offset by the current Fixed Sun Hour
173 // Fixed Sun Hour needs to be scaled to reflect the user configured Seconds Per Sun Cycle
174 PosTime += (ulong)((m_SunFixedHour / 24.0) * (ulong)SecondsPerSunCycle);
175 }
176 else
177 {
178 if (m_DayTimeSunHourScale != 0.5f)
179 {
180 ulong CurDaySeconds = CurrentTime % SecondsPerSunCycle;
181 double CurDayPercentage = (double)CurDaySeconds / SecondsPerSunCycle;
182  
183 ulong DayLightSeconds = (ulong)(m_DayTimeSunHourScale * SecondsPerSunCycle);
184 ulong NightSeconds = SecondsPerSunCycle - DayLightSeconds;
185  
186 PosTime = CurrentTime / SecondsPerSunCycle;
187 PosTime *= SecondsPerSunCycle;
188  
189 if (CurDayPercentage < 0.5)
190 {
191 PosTime += (ulong)((CurDayPercentage / .5) * DayLightSeconds);
192 }
193 else
194 {
195 PosTime += DayLightSeconds;
196 PosTime += (ulong)(((CurDayPercentage - 0.5) / .5) * NightSeconds);
197 }
198 }
199 }
200  
201 TotalDistanceTravelled = SunSpeed * PosTime; // distance measured in radians
202  
203 OrbitalPosition = (float)(TotalDistanceTravelled % m_SunCycle); // position measured in radians
204  
205 // TotalDistanceTravelled += HoursToRadians-(0.25*Math.PI)*Math.Cos(HoursToRadians)-OrbitalPosition;
206 // OrbitalPosition = (float) (TotalDistanceTravelled%SunCycle);
207  
208 SeasonalOffset = SeasonSpeed * PosTime; // Present season determined as total radians travelled around season cycle
209 Tilt.W = (float)(m_AverageTilt + (m_SeasonalTilt * Math.Sin(SeasonalOffset))); // Calculate seasonal orbital N/S tilt
210  
211 // m_log.Debug("[SUN] Total distance travelled = "+TotalDistanceTravelled+", present position = "+OrbitalPosition+".");
212 // m_log.Debug("[SUN] Total seasonal progress = "+SeasonalOffset+", present tilt = "+Tilt.W+".");
213  
214 // The sun rotates about the Z axis
215  
216 Position.X = (float)Math.Cos(-TotalDistanceTravelled);
217 Position.Y = (float)Math.Sin(-TotalDistanceTravelled);
218 Position.Z = 0;
219  
220 // For interest we rotate it slightly about the X access.
221 // Celestial tilt is a value that ranges .025
222  
223 Position *= Tilt;
224  
225 // Finally we shift the axis so that more of the
226 // circle is above the horizon than below. This
227 // makes the nights shorter than the days.
228  
229 Position = Vector3.Normalize(Position);
230 Position.Z = Position.Z + (float)HorizonShift;
231 Position = Vector3.Normalize(Position);
232  
233 // m_log.Debug("[SUN] Position("+Position.X+","+Position.Y+","+Position.Z+")");
234  
235 Velocity.X = 0;
236 Velocity.Y = 0;
237 Velocity.Z = (float)SunSpeed;
238  
239 // Correct angular velocity to reflect the seasonal rotation
240  
241 Magnitude = Position.Length();
242 if (m_SunFixed)
243 {
244 Velocity.X = 0;
245 Velocity.Y = 0;
246 Velocity.Z = 0;
247 }
248 else
249 {
250 Velocity = (Velocity * Tilt) * (1.0f / Magnitude);
251 }
252  
253 // TODO: Decouple this, so we can get rid of Linden Hour info
254 // Update Region with new Sun Vector
255 // set estate settings for region access to sun position
256 if (receivedEstateToolsSunUpdate)
257 {
258 m_scene.RegionInfo.RegionSettings.SunVector = Position;
259 }
260 }
261  
262 private float GetCurrentTimeAsLindenSunHour()
263 {
264 float curtime = m_SunFixed ? m_SunFixedHour : GetCurrentSunHour();
265 return (curtime + 6.0f) % 24.0f;
266 }
267  
268 #region INonSharedRegion Methods
269  
270 // Called immediately after the module is loaded for a given region
271 // i.e. Immediately after instance creation.
272 public void Initialise(IConfigSource config)
273 {
274 m_frame = 0;
275  
276 // This one puts an entry in the main help screen
277 // m_scene.AddCommand("Regions", this, "sun", "sun", "Usage: sun [param] [value] - Get or Update Sun module paramater", null);
278  
279 TimeZone local = TimeZone.CurrentTimeZone;
280 TicksUTCOffset = local.GetUtcOffset(local.ToLocalTime(DateTime.Now)).Ticks;
281 m_log.DebugFormat("[SUN]: localtime offset is {0}", TicksUTCOffset);
282  
283 // Align ticks with Second Life
284  
285 TicksToEpoch = new DateTime(1970, 1, 1).Ticks;
286  
287 // Just in case they don't have the stanzas
288 try
289 {
290 // Mode: determines how the sun is handled
291 // m_latitude = config.Configs["Sun"].GetDouble("latitude", d_latitude);
292 // Mode: determines how the sun is handled
293 // m_longitude = config.Configs["Sun"].GetDouble("longitude", d_longitude);
294 // Year length in days
295 m_YearLengthDays = config.Configs["Sun"].GetInt("year_length", d_year_length);
296 // Day length in decimal hours
297 m_DayLengthHours = config.Configs["Sun"].GetDouble("day_length", d_day_length);
298  
299 // Horizon shift, this is used to shift the sun's orbit, this affects the day / night ratio
300 // must hard code to ~.5 to match sun position in LL based viewers
301 m_HorizonShift = config.Configs["Sun"].GetDouble("day_night_offset", d_day_night);
302  
303 // Scales the sun hours 0...12 vs 12...24, essentially makes daylight hours longer/shorter vs nighttime hours
304 m_DayTimeSunHourScale = config.Configs["Sun"].GetDouble("day_time_sun_hour_scale", d_DayTimeSunHourScale);
305  
306 // Update frequency in frames
307 m_UpdateInterval = config.Configs["Sun"].GetInt("update_interval", d_frame_mod);
308 }
309 catch (Exception e)
310 {
311 m_log.Debug("[SUN]: Configuration access failed, using defaults. Reason: " + e.Message);
312 m_YearLengthDays = d_year_length;
313 m_DayLengthHours = d_day_length;
314 m_HorizonShift = d_day_night;
315 m_UpdateInterval = d_frame_mod;
316 m_DayTimeSunHourScale = d_DayTimeSunHourScale;
317  
318 // m_latitude = d_latitude;
319 // m_longitude = d_longitude;
320 }
321  
322 SecondsPerSunCycle = (uint) (m_DayLengthHours * 60 * 60);
323 SecondsPerYear = (uint) (SecondsPerSunCycle*m_YearLengthDays);
324  
325 // Ration of real-to-virtual time
326  
327 // VWTimeRatio = 24/m_day_length;
328  
329 // Speed of rotation needed to complete a cycle in the
330 // designated period (day and season)
331  
332 SunSpeed = m_SunCycle/SecondsPerSunCycle;
333 SeasonSpeed = m_SeasonalCycle/SecondsPerYear;
334  
335 // Horizon translation
336  
337 HorizonShift = m_HorizonShift; // Z axis translation
338 // HoursToRadians = (SunCycle/24)*VWTimeRatio;
339  
340 m_log.Debug("[SUN]: Initialization completed. Day is " + SecondsPerSunCycle + " seconds, and year is " + m_YearLengthDays + " days");
341 m_log.Debug("[SUN]: Axis offset is " + m_HorizonShift);
342 m_log.Debug("[SUN]: Percentage of time for daylight " + m_DayTimeSunHourScale);
343 m_log.Debug("[SUN]: Positional data updated every " + m_UpdateInterval + " frames");
344 }
345  
346 public Type ReplaceableInterface
347 {
348 get { return null; }
349 }
350  
351 public void AddRegion(Scene scene)
352 {
353 m_scene = scene;
354 // Insert our event handling hooks
355  
356 scene.EventManager.OnFrame += SunUpdate;
357 scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel;
358 scene.EventManager.OnEstateToolsSunUpdate += EstateToolsSunUpdate;
359 scene.EventManager.OnGetCurrentTimeAsLindenSunHour += GetCurrentTimeAsLindenSunHour;
360  
361 scene.RegisterModuleInterface<ISunModule>(this);
362  
363 // This one enables the ability to type just "sun" without any parameters
364 // m_scene.AddCommand("Regions", this, "sun", "", "", HandleSunConsoleCommand);
365 foreach (KeyValuePair<string, string> kvp in GetParamList())
366 {
367 string sunCommand = string.Format("sun {0}", kvp.Key);
368 m_scene.AddCommand("Regions", this, sunCommand, string.Format("{0} [<value>]", sunCommand), kvp.Value, "", HandleSunConsoleCommand);
369 }
370 m_scene.AddCommand("Regions", this, "sun help", "sun help", "list parameters that can be changed", "", HandleSunConsoleCommand);
371 m_scene.AddCommand("Regions", this, "sun list", "sun list", "list parameters that can be changed", "", HandleSunConsoleCommand);
372 ready = true;
373 }
374  
375 public void RemoveRegion(Scene scene)
376 {
377 ready = false;
378  
379 // Remove our hooks
380 m_scene.EventManager.OnFrame -= SunUpdate;
381 m_scene.EventManager.OnAvatarEnteringNewParcel -= AvatarEnteringParcel;
382 m_scene.EventManager.OnEstateToolsSunUpdate -= EstateToolsSunUpdate;
383 m_scene.EventManager.OnGetCurrentTimeAsLindenSunHour -= GetCurrentTimeAsLindenSunHour;
384 }
385  
386 public void RegionLoaded(Scene scene)
387 {
388 }
389  
390 public void Close()
391 {
392 }
393  
394 public string Name
395 {
396 get { return "SunModule"; }
397 }
398  
399 #endregion
400  
401 #region EventManager Events
402  
403 public void SunToClient(IClientAPI client)
404 {
405 if (ready)
406 {
407 if (m_SunFixed)
408 {
409 // m_log.DebugFormat("[SUN]: Fixed SunHour {0}, Position {1}, PosTime {2}, OrbitalPosition : {3} ",
410 // m_SunFixedHour, Position.ToString(), PosTime.ToString(), OrbitalPosition.ToString());
411 client.SendSunPos(Position, Velocity, PosTime, SecondsPerSunCycle, SecondsPerYear, OrbitalPosition);
412 }
413 else
414 {
415 // m_log.DebugFormat("[SUN]: SunHour {0}, Position {1}, PosTime {2}, OrbitalPosition : {3} ",
416 // m_SunFixedHour, Position.ToString(), PosTime.ToString(), OrbitalPosition.ToString());
417 client.SendSunPos(Position, Velocity, CurrentTime, SecondsPerSunCycle, SecondsPerYear, OrbitalPosition);
418 }
419 }
420 }
421  
422 public void SunUpdate()
423 {
424 if (((m_frame++ % m_UpdateInterval) != 0) || !ready || m_SunFixed || !receivedEstateToolsSunUpdate)
425 return;
426  
427 GenSunPos(); // Generate shared values once
428  
429 SunUpdateToAllClients();
430 }
431  
432 /// <summary>
433 /// When an avatar enters the region, it's probably a good idea to send them the current sun info
434 /// </summary>
435 /// <param name="avatar"></param>
436 /// <param name="localLandID"></param>
437 /// <param name="regionID"></param>
438 private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, UUID regionID)
439 {
440 SunToClient(avatar.ControllingClient);
441 }
442  
443 public void EstateToolsSunUpdate(ulong regionHandle)
444 {
445 if (m_scene.RegionInfo.RegionHandle == regionHandle)
446 {
447 float sunFixedHour;
448 bool fixedSun;
449  
450 if (m_scene.RegionInfo.RegionSettings.UseEstateSun)
451 {
452 sunFixedHour = (float)m_scene.RegionInfo.EstateSettings.SunPosition;
453 fixedSun = m_scene.RegionInfo.EstateSettings.FixedSun;
454 }
455 else
456 {
457 sunFixedHour = (float)m_scene.RegionInfo.RegionSettings.SunPosition - 6.0f;
458 fixedSun = m_scene.RegionInfo.RegionSettings.FixedSun;
459 }
460  
461 // Must limit the Sun Hour to 0 ... 24
462 while (sunFixedHour > 24.0f)
463 sunFixedHour -= 24;
464  
465 while (sunFixedHour < 0)
466 sunFixedHour += 24;
467  
468 m_SunFixedHour = sunFixedHour;
469 m_SunFixed = fixedSun;
470  
471 // m_log.DebugFormat("[SUN]: Sun Settings Update: Fixed Sun? : {0}", m_SunFixed.ToString());
472 // m_log.DebugFormat("[SUN]: Sun Settings Update: Sun Hour : {0}", m_SunFixedHour.ToString());
473  
474 receivedEstateToolsSunUpdate = true;
475  
476 // Generate shared values
477 GenSunPos();
478  
479 // When sun settings are updated, we should update all clients with new settings.
480 SunUpdateToAllClients();
481  
482 // m_log.DebugFormat("[SUN]: PosTime : {0}", PosTime.ToString());
483 }
484 }
485  
486 #endregion
487  
488 private void SunUpdateToAllClients()
489 {
490 m_scene.ForEachRootClient(delegate(IClientAPI client)
491 {
492 SunToClient(client);
493 });
494 }
495  
496 #region ISunModule Members
497  
498 public double GetSunParameter(string param)
499 {
500 switch (param.ToLower())
501 {
502 case "year_length":
503 return m_YearLengthDays;
504  
505 case "day_length":
506 return m_DayLengthHours;
507  
508 case "day_night_offset":
509 return m_HorizonShift;
510  
511 case "day_time_sun_hour_scale":
512 return m_DayTimeSunHourScale;
513  
514 case "update_interval":
515 return m_UpdateInterval;
516  
517 case "current_time":
518 return GetCurrentTimeAsLindenSunHour();
519  
520 default:
521 throw new Exception("Unknown sun parameter.");
522 }
523 }
524  
525 public void SetSunParameter(string param, double value)
526 {
527 switch (param)
528 {
529 case "year_length":
530 m_YearLengthDays = (int)value;
531 SecondsPerYear = (uint) (SecondsPerSunCycle*m_YearLengthDays);
532 SeasonSpeed = m_SeasonalCycle/SecondsPerYear;
533 break;
534  
535 case "day_length":
536 m_DayLengthHours = value;
537 SecondsPerSunCycle = (uint) (m_DayLengthHours * 60 * 60);
538 SecondsPerYear = (uint) (SecondsPerSunCycle*m_YearLengthDays);
539 SunSpeed = m_SunCycle/SecondsPerSunCycle;
540 SeasonSpeed = m_SeasonalCycle/SecondsPerYear;
541 break;
542  
543 case "day_night_offset":
544 m_HorizonShift = value;
545 HorizonShift = m_HorizonShift;
546 break;
547  
548 case "day_time_sun_hour_scale":
549 m_DayTimeSunHourScale = value;
550 break;
551  
552 case "update_interval":
553 m_UpdateInterval = (int)value;
554 break;
555  
556 case "current_time":
557 value = (value + 18.0) % 24.0;
558 // set the current offset so that the effective sun time is the parameter
559 m_CurrentTimeOffset = 0; // clear this first so we use raw time
560 m_CurrentTimeOffset = (ulong)(SecondsPerSunCycle * value/ 24.0) - (CurrentTime % SecondsPerSunCycle);
561 break;
562  
563 default:
564 throw new Exception("Unknown sun parameter.");
565  
566 // Generate shared values
567 GenSunPos();
568  
569 // When sun settings are updated, we should update all clients with new settings.
570 SunUpdateToAllClients();
571 }
572 }
573  
574 public float GetCurrentSunHour()
575 {
576 float ticksleftover = CurrentTime % SecondsPerSunCycle;
577  
578 return (24.0f * (ticksleftover / SecondsPerSunCycle));
579 }
580  
581 #endregion
582  
583 public void HandleSunConsoleCommand(string module, string[] cmdparams)
584 {
585 if (m_scene.ConsoleScene() == null)
586 {
587 // FIXME: If console region is root then this will be printed by every module. Currently, there is no
588 // way to prevent this, short of making the entire module shared (which is complete overkill).
589 // One possibility is to return a bool to signal whether the module has completely handled the command
590 m_log.InfoFormat("[Sun]: Please change to a specific region in order to set Sun parameters.");
591 return;
592 }
593  
594 if (m_scene.ConsoleScene() != m_scene)
595 {
596 m_log.InfoFormat("[Sun]: Console Scene is not my scene.");
597 return;
598 }
599  
600 m_log.InfoFormat("[Sun]: Processing command.");
601  
602 foreach (string output in ParseCmdParams(cmdparams))
603 {
604 MainConsole.Instance.Output(output);
605 }
606 }
607  
608 private Dictionary<string, string> GetParamList()
609 {
610 Dictionary<string, string> Params = new Dictionary<string, string>();
611  
612 Params.Add("year_length", "number of days to a year");
613 Params.Add("day_length", "number of hours to a day");
614 Params.Add("day_night_offset", "induces a horizon shift");
615 Params.Add("update_interval", "how often to update the sun's position in frames");
616 Params.Add("day_time_sun_hour_scale", "scales day light vs nite hours to change day/night ratio");
617 Params.Add("current_time", "time in seconds of the simulator");
618  
619 return Params;
620 }
621  
622 private List<string> ParseCmdParams(string[] args)
623 {
624 List<string> Output = new List<string>();
625  
626 if ((args.Length == 1) || (args[1].ToLower() == "help") || (args[1].ToLower() == "list"))
627 {
628 Output.Add("The following parameters can be changed or viewed:");
629 foreach (KeyValuePair<string, string> kvp in GetParamList())
630 {
631 Output.Add(String.Format("{0} - {1}",kvp.Key, kvp.Value));
632 }
633 return Output;
634 }
635  
636 if (args.Length == 2)
637 {
638 try
639 {
640 double value = GetSunParameter(args[1]);
641 Output.Add(String.Format("Parameter {0} is {1}.", args[1], value.ToString()));
642 }
643 catch (Exception)
644 {
645 Output.Add(String.Format("Unknown parameter {0}.", args[1]));
646 }
647  
648 }
649 else if (args.Length == 3)
650 {
651 double value = 0.0;
652 if (! double.TryParse(args[2], out value))
653 {
654 Output.Add(String.Format("The parameter value {0} is not a valid number.", args[2]));
655 return Output;
656 }
657  
658 SetSunParameter(args[1].ToLower(), value);
659 Output.Add(String.Format("Parameter {0} set to {1}.", args[1], value.ToString()));
660 }
661  
662 return Output;
663 }
664 }
665 }