aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region
diff options
context:
space:
mode:
authorlbsa712007-09-17 06:57:17 +0000
committerlbsa712007-09-17 06:57:17 +0000
commit6961013c249e7761060bec71ad25d5968424e812 (patch)
tree4306987f6229b83e2a23ad236586522a77327dbc /OpenSim/Region
parentadded version (diff)
downloadopensim-SC-6961013c249e7761060bec71ad25d5968424e812.zip
opensim-SC-6961013c249e7761060bec71ad25d5968424e812.tar.gz
opensim-SC-6961013c249e7761060bec71ad25d5968424e812.tar.bz2
opensim-SC-6961013c249e7761060bec71ad25d5968424e812.tar.xz
* CHANGED SOME CONSOLE COMMAND BEHAVIOURS
* Normalized 'change-region' so (almost) all commands are context sensitive (use 'root' or '..' to set 'all scenes' context) * 'terrain-sim' is thusly obsolete, use 'change-region', followed by 'terrain' * Introduced SceneManager to administrate operations on group of scenes and moved relevant funcs there. * In it, there's a ForEach(Action<Scene>) that either passes all scenes, or only current scene depending on context. * Changed default prim backup (save-xml/load-xml) xml to "prim-backup.xml" * Changed Disable/EnablePermissions to BypassPermissions = true/false; Also: * Removed unused and non-existent project ref
Diffstat (limited to 'OpenSim/Region')
-rw-r--r--OpenSim/Region/Application/OpenSimMain.cs214
-rw-r--r--OpenSim/Region/Environment/PermissionManager.cs36
-rw-r--r--OpenSim/Region/Environment/Scenes/SceneManager.cs210
3 files changed, 316 insertions, 144 deletions
diff --git a/OpenSim/Region/Application/OpenSimMain.cs b/OpenSim/Region/Application/OpenSimMain.cs
index 8327e26..4226393 100644
--- a/OpenSim/Region/Application/OpenSimMain.cs
+++ b/OpenSim/Region/Application/OpenSimMain.cs
@@ -50,6 +50,8 @@ namespace OpenSim
50 50
51 public class OpenSimMain : RegionApplicationBase, conscmd_callback 51 public class OpenSimMain : RegionApplicationBase, conscmd_callback
52 { 52 {
53 private const string DEFAULT_PRIM_BACKUP_FILENAME = "prim-backup.xml";
54
53 public string m_physicsEngine; 55 public string m_physicsEngine;
54 public string m_scriptEngine; 56 public string m_scriptEngine;
55 public bool m_sandbox; 57 public bool m_sandbox;
@@ -65,7 +67,7 @@ namespace OpenSim
65 67
66 protected List<UDPServer> m_udpServers = new List<UDPServer>(); 68 protected List<UDPServer> m_udpServers = new List<UDPServer>();
67 protected List<RegionInfo> m_regionData = new List<RegionInfo>(); 69 protected List<RegionInfo> m_regionData = new List<RegionInfo>();
68 protected List<Scene> m_localScenes = new List<Scene>(); 70 protected SceneManager m_localScenes = new SceneManager();
69 71
70 private bool m_silent; 72 private bool m_silent;
71 private readonly string m_logFilename = ("region-console.log"); 73 private readonly string m_logFilename = ("region-console.log");
@@ -213,10 +215,7 @@ namespace OpenSim
213 //} 215 //}
214 216
215 //Server side object editing permissions checking 217 //Server side object editing permissions checking
216 if (m_permissions) 218 scene.PermissionsMngr.BypassPermissions = !m_permissions;
217 scene.PermissionsMngr.EnablePermissions();
218 else
219 scene.PermissionsMngr.DisablePermissions();
220 219
221 m_localScenes.Add(scene); 220 m_localScenes.Add(scene);
222 221
@@ -266,7 +265,7 @@ namespace OpenSim
266 IAssetServer assetServer; 265 IAssetServer assetServer;
267 if (m_assetStorage == "db4o") 266 if (m_assetStorage == "db4o")
268 { 267 {
269 assetServer = new LocalAssetServer(); 268 assetServer = new LocalAssetServer();
270 } 269 }
271 else 270 else
272 { 271 {
@@ -334,10 +333,9 @@ namespace OpenSim
334 m_log.Verbose("Killing clients"); 333 m_log.Verbose("Killing clients");
335 // IMPLEMENT THIS 334 // IMPLEMENT THIS
336 m_log.Verbose("Closing console and terminating"); 335 m_log.Verbose("Closing console and terminating");
337 for (int i = 0; i < m_localScenes.Count; i++) 336
338 { 337 m_localScenes.Close();
339 m_localScenes[i].Close(); 338
340 }
341 m_log.Close(); 339 m_log.Close();
342 Environment.Exit(0); 340 Environment.Exit(0);
343 } 341 }
@@ -376,6 +374,8 @@ namespace OpenSim
376 /// <param name="cmdparams">Additional arguments passed to the command</param> 374 /// <param name="cmdparams">Additional arguments passed to the command</param>
377 public void RunCmd(string command, string[] cmdparams) 375 public void RunCmd(string command, string[] cmdparams)
378 { 376 {
377 string result = "";
378
379 if ((m_consoleRegion == null) || (command == "change-region") || (command == "shutdown")) 379 if ((m_consoleRegion == null) || (command == "change-region") || (command == "shutdown"))
380 { 380 {
381 switch (command) 381 switch (command)
@@ -386,7 +386,7 @@ namespace OpenSim
386 Debug(cmdparams); 386 Debug(cmdparams);
387 } 387 }
388 break; 388 break;
389 389
390 case "help": 390 case "help":
391 m_log.Error("alert - send alert to a designated user or all users."); 391 m_log.Error("alert - send alert to a designated user or all users.");
392 m_log.Error(" alert [First] [Last] [Message] - send an alert to a user. Case sensitive."); 392 m_log.Error(" alert [First] [Last] [Message] - send an alert to a user. Case sensitive.");
@@ -414,56 +414,51 @@ namespace OpenSim
414 case "save-xml": 414 case "save-xml":
415 if (cmdparams.Length > 0) 415 if (cmdparams.Length > 0)
416 { 416 {
417 m_localScenes[0].SavePrimsToXml(cmdparams[0]); 417 m_localScenes.SavePrimsToXml(cmdparams[0]);
418 } 418 }
419 else 419 else
420 { 420 {
421 m_localScenes[0].SavePrimsToXml("test.xml"); 421 m_localScenes.SavePrimsToXml(DEFAULT_PRIM_BACKUP_FILENAME);
422 } 422 }
423 break; 423 break;
424 424
425 case "load-xml": 425 case "load-xml":
426 if (cmdparams.Length > 0) 426 if (cmdparams.Length > 0)
427 { 427 {
428 m_localScenes[0].LoadPrimsFromXml(cmdparams[0]); 428 m_localScenes.LoadPrimsFromXml(cmdparams[0]);
429 } 429 }
430 else 430 else
431 { 431 {
432 m_localScenes[0].LoadPrimsFromXml("test.xml"); 432 m_localScenes.LoadPrimsFromXml(DEFAULT_PRIM_BACKUP_FILENAME);
433 } 433 }
434 break; 434 break;
435 435
436 case "terrain": 436 case "terrain":
437 string result = ""; 437 if (!m_localScenes.RunTerrainCmd(cmdparams, ref result))
438 foreach (Scene scene in m_localScenes)
439 { 438 {
440 if (!scene.Terrain.RunTerrainCmd(cmdparams, ref result, scene.RegionInfo.RegionName)) 439 m_log.Error(result);
441 {
442 m_log.Error(result);
443 }
444 } 440 }
445 break; 441 break;
446 case "terrain-sim":
447 string result2 = "";
448 foreach (Scene scene in m_localScenes)
449 {
450 if (scene.RegionInfo.RegionName.ToLower() == cmdparams[0].ToLower())
451 {
452 string[] tmpCmdparams = new string[cmdparams.Length - 1];
453 cmdparams.CopyTo(tmpCmdparams, 1);
454 442
455 if (!scene.Terrain.RunTerrainCmd(tmpCmdparams, ref result2, scene.RegionInfo.RegionName)) 443 // terrain-sim now obsolete: do change-region first, then terrain as usual
456 { 444 //case "terrain-sim":
457 m_log.Error(result2); 445 // foreach (Scene scene in m_localScenes)
458 } 446 // {
459 } 447 // if (scene.RegionInfo.RegionName.ToLower() == cmdparams[0].ToLower())
460 } 448 // {
461 break; 449 // string[] tmpCmdparams = new string[cmdparams.Length - 1];
450 // cmdparams.CopyTo(tmpCmdparams, 1);
451
452 // if (!scene.Terrain.RunTerrainCmd(tmpCmdparams, ref result, scene.RegionInfo.RegionName))
453 // {
454 // m_log.Error(result);
455 // }
456 // }
457 // }
458 // break;
459
462 case "script": 460 case "script":
463 foreach (Scene scene in m_localScenes) 461 m_localScenes.SendCommandToScripts(cmdparams);
464 {
465 scene.SendCommandToScripts(cmdparams);
466 }
467 break; 462 break;
468 463
469 case "command-script": 464 case "command-script":
@@ -475,27 +470,18 @@ namespace OpenSim
475 470
476 case "permissions": 471 case "permissions":
477 // Treats each user as a super-admin when disabled 472 // Treats each user as a super-admin when disabled
478 foreach (Scene scene in m_localScenes) 473 bool permissions = Convert.ToBoolean(cmdparams[0]);
479 { 474
480 if (Convert.ToBoolean(cmdparams[0])) 475 m_localScenes.BypassPermissions(!permissions);
481 scene.PermissionsMngr.EnablePermissions(); 476
482 else
483 scene.PermissionsMngr.DisablePermissions();
484 }
485 break; 477 break;
486 478
487 case "backup": 479 case "backup":
488 foreach (Scene scene in m_localScenes) 480 m_localScenes.Backup();
489 {
490 scene.Backup();
491 }
492 break; 481 break;
493 482
494 case "alert": 483 case "alert":
495 foreach (Scene scene in m_localScenes) 484 m_localScenes.HandleAlertCommand(cmdparams);
496 {
497 scene.HandleAlertCommand(cmdparams);
498 }
499 break; 485 break;
500 486
501 case "create": 487 case "create":
@@ -513,43 +499,27 @@ namespace OpenSim
513 case "change-region": 499 case "change-region":
514 if (cmdparams.Length > 0) 500 if (cmdparams.Length > 0)
515 { 501 {
516 if ((cmdparams[0].ToLower() == "root") || (cmdparams[0].ToLower() == "..")) 502 string regionName = this.CombineParams(cmdparams, 0);
503
504 if( m_localScenes.TrySetCurrentRegion( regionName ) )
517 { 505 {
518 if (m_consoleRegion != null) 506
519 {
520 m_consoleRegion = null;
521 MainLog.Instance.Verbose("Now at Root level");
522 }
523 else
524 {
525 MainLog.Instance.Verbose("Already at Root level");
526 }
527 } 507 }
528 else 508 else
529 { 509 {
530 string name = this.CombineParams(cmdparams, 0); 510 MainLog.Instance.Error("Couldn't set current region to: " + regionName);
531 Console.WriteLine("Searching for Region: '" + name + "'");
532 foreach (Scene scene in m_localScenes)
533 {
534 if (scene.RegionInfo.RegionName.ToLower() == name.ToLower())
535 {
536 m_consoleRegion = scene;
537 MainLog.Instance.Verbose("Setting current region: " + m_consoleRegion.RegionInfo.RegionName);
538 }
539 }
540 } 511 }
541 } 512 }
513
514 if (m_localScenes.CurrentScene == null)
515 {
516 MainLog.Instance.Verbose("Currently at Root level. To change region please use 'change-region <regioname>'");
517 }
542 else 518 else
543 { 519 {
544 if (m_consoleRegion != null) 520 MainLog.Instance.Verbose("Current Region: " + m_consoleRegion.RegionInfo.RegionName + ". To change region please use 'change-region <regioname>'");
545 {
546 MainLog.Instance.Verbose("Current Region: " + m_consoleRegion.RegionInfo.RegionName + ". To change region please use 'change-region <regioname>'");
547 }
548 else
549 {
550 MainLog.Instance.Verbose("Currently at Root level. To change region please use 'change-region <regioname>'");
551 }
552 } 521 }
522
553 break; 523 break;
554 524
555 default: 525 default:
@@ -564,47 +534,26 @@ namespace OpenSim
564 } 534 }
565 } 535 }
566 536
567 private void DebugPacket(int newDebug)
568 {
569 for (int i = 0; i < m_localScenes.Count; i++)
570 {
571 Scene scene = m_localScenes[i];
572 foreach (EntityBase entity in scene.Entities.Values)
573 {
574 if (entity is ScenePresence)
575 {
576 ScenePresence scenePrescence = entity as ScenePresence;
577 if (!scenePrescence.childAgent)
578 {
579 m_log.Error(String.Format("Packet debug for {0} {1} set to {2}",
580 scenePrescence.Firstname, scenePrescence.Lastname,
581 newDebug));
582 scenePrescence.ControllingClient.SetDebug(newDebug);
583 }
584 }
585 }
586 }
587 }
588 public void Debug(string[] args) 537 public void Debug(string[] args)
589 { 538 {
590 switch(args[0]) 539 switch (args[0])
591 { 540 {
592 case "packet": 541 case "packet":
593 if (args.Length > 1) 542 if (args.Length > 1)
594 { 543 {
595 int newDebug; 544 int newDebug;
596 if (int.TryParse(args[1], out newDebug)) 545 if (int.TryParse(args[1], out newDebug))
597 { 546 {
598 DebugPacket(newDebug); 547 m_localScenes.DebugPacket(m_log, newDebug);
599 } 548 }
600 else 549 else
601 { 550 {
602 m_log.Error("packet debug should be 0..2"); 551 m_log.Error("packet debug should be 0..2");
603 } 552 }
604 System.Console.WriteLine("New packet debug: " + newDebug.ToString()); 553 System.Console.WriteLine("New packet debug: " + newDebug.ToString());
605 554
606 } 555 }
607 556
608 break; 557 break;
609 default: 558 default:
610 m_log.Error("Unknown debug"); 559 m_log.Error("Unknown debug");
@@ -627,29 +576,34 @@ namespace OpenSim
627 break; 576 break;
628 case "users": 577 case "users":
629 m_log.Error(String.Format("{0,-16}{1,-16}{2,-25}{3,-25}{4,-16}{5,-16}{6,-16}", "Firstname", "Lastname", "Agent ID", "Session ID", "Circuit", "IP", "World")); 578 m_log.Error(String.Format("{0,-16}{1,-16}{2,-25}{3,-25}{4,-16}{5,-16}{6,-16}", "Firstname", "Lastname", "Agent ID", "Session ID", "Circuit", "IP", "World"));
630 for (int i = 0; i < m_localScenes.Count; i++) 579
580 List<ScenePresence> avatars = m_localScenes.GetAvatars();
581
582 foreach( ScenePresence avatar in avatars )
631 { 583 {
632 Scene scene = m_localScenes[i]; 584 RegionInfo regionInfo = m_localScenes.GetRegionInfo( avatar.RegionHandle );
633 foreach (EntityBase entity in scene.Entities.Values) 585 string regionName;
586
587 if( regionInfo == null )
634 { 588 {
635 if (entity is ScenePresence) 589 regionName = "Unresolvable";
636 {
637 ScenePresence scenePrescence = entity as ScenePresence;
638 if (!scenePrescence.childAgent)
639 {
640 m_log.Error(
641 String.Format("{0,-16}{1,-16}{2,-25}{3,-25}{4,-16},{5,-16}{6,-16}",
642 scenePrescence.Firstname,
643 scenePrescence.Lastname,
644 scenePrescence.UUID,
645 scenePrescence.ControllingClient.AgentId,
646 "Unknown",
647 "Unknown",
648 scene.RegionInfo.RegionName));
649 }
650 }
651 } 590 }
591 else
592 {
593 regionName = regionInfo.RegionName;
594 }
595
596 m_log.Error(
597 String.Format("{0,-16}{1,-16}{2,-25}{3,-25}{4,-16},{5,-16}{6,-16}",
598 avatar.Firstname,
599 avatar.Lastname,
600 avatar.UUID,
601 avatar.ControllingClient.AgentId,
602 "Unknown",
603 "Unknown",
604 regionName ));
652 } 605 }
606
653 break; 607 break;
654 case "modules": 608 case "modules":
655 m_log.Error("The currently loaded shared modules are:"); 609 m_log.Error("The currently loaded shared modules are:");
diff --git a/OpenSim/Region/Environment/PermissionManager.cs b/OpenSim/Region/Environment/PermissionManager.cs
index 3e481e8..110a130 100644
--- a/OpenSim/Region/Environment/PermissionManager.cs
+++ b/OpenSim/Region/Environment/PermissionManager.cs
@@ -19,21 +19,17 @@ namespace OpenSim.Region.Environment
19 // disable in any production environment 19 // disable in any production environment
20 // TODO: Change this to false when permissions are a desired default 20 // TODO: Change this to false when permissions are a desired default
21 // TODO: Move to configuration option. 21 // TODO: Move to configuration option.
22 private bool bypassPermissions = true; 22 private bool m_bypassPermissions = true;
23 23 public bool BypassPermissions
24 public PermissionManager(Scene scene)
25 { 24 {
26 m_scene = scene; 25 get { return m_bypassPermissions; }
26 set { m_bypassPermissions = value; }
27 } 27 }
28 28
29 public void DisablePermissions()
30 {
31 bypassPermissions = true;
32 }
33 29
34 public void EnablePermissions() 30 public PermissionManager(Scene scene)
35 { 31 {
36 bypassPermissions = false; 32 m_scene = scene;
37 } 33 }
38 34
39 protected virtual void SendPermissionError(LLUUID user, string reason) 35 protected virtual void SendPermissionError(LLUUID user, string reason)
@@ -43,16 +39,20 @@ namespace OpenSim.Region.Environment
43 39
44 protected virtual bool IsAdministrator(LLUUID user) 40 protected virtual bool IsAdministrator(LLUUID user)
45 { 41 {
46 if (bypassPermissions) 42 if (m_bypassPermissions)
47 return bypassPermissions; 43 {
44 return true;
45 }
48 46
49 return m_scene.RegionInfo.MasterAvatarAssignedUUID == user; 47 return m_scene.RegionInfo.MasterAvatarAssignedUUID == user;
50 } 48 }
51 49
52 protected virtual bool IsEstateManager(LLUUID user) 50 protected virtual bool IsEstateManager(LLUUID user)
53 { 51 {
54 if (bypassPermissions) 52 if (m_bypassPermissions)
55 return bypassPermissions; 53 {
54 return true;
55 }
56 56
57 return false; 57 return false;
58 } 58 }
@@ -74,14 +74,22 @@ namespace OpenSim.Region.Environment
74 string reason = "Insufficient permission"; 74 string reason = "Insufficient permission";
75 75
76 if (IsAdministrator(user)) 76 if (IsAdministrator(user))
77 {
77 permission = true; 78 permission = true;
79 }
78 else 80 else
81 {
79 reason = "Not an administrator"; 82 reason = "Not an administrator";
83 }
80 84
81 if (GenericParcelPermission(user, position)) 85 if (GenericParcelPermission(user, position))
86 {
82 permission = true; 87 permission = true;
88 }
83 else 89 else
90 {
84 reason = "Not the parcel owner"; 91 reason = "Not the parcel owner";
92 }
85 93
86 if (!permission) 94 if (!permission)
87 SendPermissionError(user, reason); 95 SendPermissionError(user, reason);
diff --git a/OpenSim/Region/Environment/Scenes/SceneManager.cs b/OpenSim/Region/Environment/Scenes/SceneManager.cs
new file mode 100644
index 0000000..9773407
--- /dev/null
+++ b/OpenSim/Region/Environment/Scenes/SceneManager.cs
@@ -0,0 +1,210 @@
1using System.Collections.Generic;
2using System;
3using OpenSim.Framework.Console;
4using OpenSim.Framework.Types;
5
6namespace OpenSim.Region.Environment.Scenes
7{
8 public class SceneManager
9 {
10 private readonly List<Scene> m_localScenes;
11 private Scene m_currentScene = null;
12 public Scene CurrentScene
13 {
14 get
15 {
16 return m_currentScene;
17 }
18 }
19
20 private Scene CurrentOrFirstScene
21 {
22 get
23 {
24 if (m_currentScene == null)
25 {
26 return m_localScenes[0];
27 }
28 else
29 {
30 return m_currentScene;
31 }
32 }
33 }
34
35 public SceneManager()
36 {
37 m_localScenes = new List<Scene>();
38 }
39
40 public void Close()
41 {
42 for (int i = 0; i < m_localScenes.Count; i++)
43 {
44 m_localScenes[i].Close();
45 }
46 }
47
48 public void Add(Scene scene)
49 {
50 m_localScenes.Add(scene);
51 }
52
53 public void SavePrimsToXml(string filename)
54 {
55 CurrentOrFirstScene.SavePrimsToXml(filename);
56 }
57
58 public void LoadPrimsFromXml(string filename)
59 {
60 CurrentOrFirstScene.LoadPrimsFromXml(filename);
61 }
62
63 public bool RunTerrainCmd(string[] cmdparams, ref string result)
64 {
65 if (m_currentScene == null)
66 {
67 bool success = true;
68 foreach (Scene scene in m_localScenes)
69 {
70 if (!scene.Terrain.RunTerrainCmd(cmdparams, ref result, scene.RegionInfo.RegionName))
71 {
72 success = false;
73 }
74 }
75
76 return success;
77 }
78 else
79 {
80 return m_currentScene.Terrain.RunTerrainCmd(cmdparams, ref result, m_currentScene.RegionInfo.RegionName);
81 }
82 }
83
84 public void SendCommandToScripts(string[] cmdparams)
85 {
86 ForEach(delegate(Scene scene)
87 {
88 scene.SendCommandToScripts(cmdparams);
89 });
90 }
91
92 public void BypassPermissions(bool bypassPermissions)
93 {
94 ForEach(delegate(Scene scene)
95 {
96 scene.PermissionsMngr.BypassPermissions = bypassPermissions;
97 });
98 }
99
100 private void ForEach(Action<Scene> func)
101 {
102 if (m_currentScene == null)
103 {
104 m_localScenes.ForEach(func);
105 }
106 else
107 {
108 func(m_currentScene);
109 }
110 }
111
112 public void Backup()
113 {
114 ForEach(delegate(Scene scene)
115 {
116 scene.Backup();
117 });
118 }
119
120 public void HandleAlertCommand(string[] cmdparams)
121 {
122 ForEach(delegate(Scene scene)
123 {
124 scene.HandleAlertCommand(cmdparams);
125 });
126 }
127
128 public bool TrySetCurrentRegion(string regionName)
129 {
130 if ((String.Compare(regionName, "root") == 0) || (String.Compare(regionName, "..") == 0))
131 {
132 m_currentScene = null;
133 return true;
134 }
135 else
136 {
137 Console.WriteLine("Searching for Region: '" + regionName + "'");
138 Scene foundScene = null;
139
140 foreach (Scene scene in m_localScenes)
141 {
142 if (String.Compare(scene.RegionInfo.RegionName, regionName, true) == 0)
143 {
144 m_currentScene = scene;
145 return true;
146 }
147 }
148
149 return false;
150 }
151 }
152
153 public void DebugPacket(LogBase log, int newDebug)
154 {
155 ForEach(delegate(Scene scene)
156 {
157 foreach (EntityBase entity in scene.Entities.Values)
158 {
159 if (entity is ScenePresence)
160 {
161 ScenePresence scenePrescence = entity as ScenePresence;
162 if (!scenePrescence.childAgent)
163 {
164 log.Error(String.Format("Packet debug for {0} {1} set to {2}",
165 scenePrescence.Firstname, scenePrescence.Lastname,
166 newDebug));
167
168 scenePrescence.ControllingClient.SetDebug(newDebug);
169 }
170 }
171 }
172 });
173 }
174
175 public List<ScenePresence> GetAvatars()
176 {
177 List<ScenePresence> avatars = new List<ScenePresence>();
178
179 ForEach(delegate(Scene scene)
180 {
181 foreach (EntityBase entity in scene.Entities.Values)
182 {
183 if (entity is ScenePresence)
184 {
185 ScenePresence scenePrescence = entity as ScenePresence;
186 if (!scenePrescence.childAgent)
187 {
188 avatars.Add(scenePrescence);
189 }
190 }
191 }
192 });
193
194 return avatars;
195 }
196
197 public RegionInfo GetRegionInfo(ulong regionHandle)
198 {
199 foreach (Scene scene in m_localScenes)
200 {
201 if( scene.RegionInfo.RegionHandle == regionHandle )
202 {
203 return scene.RegionInfo;
204 }
205 }
206
207 return null;
208 }
209 }
210}