clockwerk-opensim-stable – Blame information for rev 1

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