diff options
Diffstat (limited to 'OpenSim/Region')
-rw-r--r-- | OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs | 363 |
1 files changed, 236 insertions, 127 deletions
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs index 66811de..b8645e2 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/WorldMapModule.cs | |||
@@ -89,7 +89,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
89 | private volatile bool threadrunning = false; | 89 | private volatile bool threadrunning = false; |
90 | // expire time for the blacklists in seconds | 90 | // expire time for the blacklists in seconds |
91 | private double expireBlackListTime = 600.0; // 10 minutes | 91 | private double expireBlackListTime = 600.0; // 10 minutes |
92 | // expire mapItems responses time in seconds. If too high disturbs the green dots updates | 92 | // expire mapItems responses time in seconds. Throttles requests to regions that do answer |
93 | private const double expireResponsesTime = 120.0; // 2 minutes ? | 93 | private const double expireResponsesTime = 120.0; // 2 minutes ? |
94 | //private int CacheRegionsDistance = 256; | 94 | //private int CacheRegionsDistance = 256; |
95 | 95 | ||
@@ -465,131 +465,235 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
465 | List<mapItemReply> mapitems = new List<mapItemReply>(); | 465 | List<mapItemReply> mapitems = new List<mapItemReply>(); |
466 | mapItemReply mapitem = new mapItemReply(); | 466 | mapItemReply mapitem = new mapItemReply(); |
467 | 467 | ||
468 | bool adultRegion; | 468 | // viewers only ask for green dots to each region now |
469 | // except at login with regionhandle 0 | ||
470 | // possible on some other rare ocasions | ||
471 | // use previus hack of sending all items with the green dots | ||
469 | 472 | ||
470 | switch (itemtype) | 473 | bool adultRegion; |
474 | if (regionhandle == 0) | ||
471 | { | 475 | { |
472 | case (int)GridItemType.AgentLocations: | 476 | switch (itemtype) |
473 | // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots) | 477 | { |
478 | case (int)GridItemType.AgentLocations: | ||
479 | // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots) | ||
474 | 480 | ||
475 | int tc = Environment.TickCount; | 481 | int tc = Environment.TickCount; |
476 | if (m_scene.GetRootAgentCount() <= 1) | 482 | if (m_scene.GetRootAgentCount() <= 1) |
477 | { | 483 | { |
478 | mapitem = new mapItemReply( | 484 | mapitem = new mapItemReply( |
479 | xstart + 1, | 485 | xstart + 1, |
480 | ystart + 1, | 486 | ystart + 1, |
481 | UUID.Zero, | 487 | UUID.Zero, |
482 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | 488 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), |
483 | 0, 0); | 489 | 0, 0); |
484 | mapitems.Add(mapitem); | 490 | mapitems.Add(mapitem); |
485 | } | 491 | } |
486 | else | 492 | else |
487 | { | ||
488 | m_scene.ForEachRootScenePresence(delegate (ScenePresence sp) | ||
489 | { | 493 | { |
494 | m_scene.ForEachRootScenePresence(delegate (ScenePresence sp) | ||
495 | { | ||
490 | // Don't send a green dot for yourself | 496 | // Don't send a green dot for yourself |
491 | if (sp.UUID != remoteClient.AgentId) | 497 | if (sp.UUID != remoteClient.AgentId) |
492 | { | 498 | { |
493 | mapitem = new mapItemReply( | 499 | mapitem = new mapItemReply( |
494 | xstart + (uint)sp.AbsolutePosition.X, | 500 | xstart + (uint)sp.AbsolutePosition.X, |
495 | ystart + (uint)sp.AbsolutePosition.Y, | 501 | ystart + (uint)sp.AbsolutePosition.Y, |
496 | UUID.Zero, | 502 | UUID.Zero, |
497 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | 503 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), |
498 | 1, 0); | 504 | 1, 0); |
499 | mapitems.Add(mapitem); | 505 | mapitems.Add(mapitem); |
500 | } | 506 | } |
501 | }); | 507 | }); |
502 | } | 508 | } |
503 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | 509 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); |
504 | 510 | break; | |
505 | break; | ||
506 | 511 | ||
507 | case (int)GridItemType.AdultLandForSale: | 512 | case (int)GridItemType.Telehub: |
508 | case (int)GridItemType.LandForSale: | 513 | // Service 1 (MAP_ITEM_TELEHUB) |
509 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) | ||
510 | 514 | ||
511 | adultRegion = m_scene.RegionInfo.RegionSettings.Maturity == 2; | 515 | SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject); |
512 | if (adultRegion) | 516 | if (sog != null) |
513 | { | 517 | { |
514 | if (itemtype == (int)GridItemType.LandForSale) | 518 | mapitem = new mapItemReply( |
515 | break; | 519 | xstart + (uint)sog.AbsolutePosition.X, |
516 | } | 520 | ystart + (uint)sog.AbsolutePosition.Y, |
517 | else | 521 | UUID.Zero, |
518 | { | 522 | sog.Name, |
519 | if (itemtype == (int)GridItemType.AdultLandForSale) | 523 | 0, // color (not used) |
520 | break; | 524 | 0 // 0 = telehub / 1 = infohub |
521 | } | 525 | ); |
526 | mapitems.Add(mapitem); | ||
527 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | ||
528 | } | ||
529 | break; | ||
522 | 530 | ||
523 | // Parcels | 531 | case (int)GridItemType.AdultLandForSale: |
524 | ILandChannel landChannel = m_scene.LandChannel; | 532 | case (int)GridItemType.LandForSale: |
525 | List<ILandObject> parcels = landChannel.AllParcels(); | ||
526 | 533 | ||
527 | if ((parcels != null) && (parcels.Count >= 1)) | 534 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) |
528 | { | 535 | adultRegion = m_scene.RegionInfo.RegionSettings.Maturity == 2; |
529 | foreach (ILandObject parcel_interface in parcels) | 536 | if (adultRegion) |
537 | { | ||
538 | if (itemtype == (int)GridItemType.LandForSale) | ||
539 | break; | ||
540 | } | ||
541 | else | ||
530 | { | 542 | { |
531 | // Play it safe | 543 | if (itemtype == (int)GridItemType.AdultLandForSale) |
532 | if (!(parcel_interface is LandObject)) | 544 | break; |
533 | continue; | 545 | } |
534 | 546 | ||
535 | LandObject land = (LandObject)parcel_interface; | 547 | // Parcels |
536 | LandData parcel = land.LandData; | 548 | ILandChannel landChannel = m_scene.LandChannel; |
549 | List<ILandObject> parcels = landChannel.AllParcels(); | ||
537 | 550 | ||
538 | // Show land for sale | 551 | if ((parcels != null) && (parcels.Count >= 1)) |
539 | if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale) | 552 | { |
553 | foreach (ILandObject parcel_interface in parcels) | ||
540 | { | 554 | { |
541 | Vector3 min = parcel.AABBMin; | 555 | // Play it safe |
542 | Vector3 max = parcel.AABBMax; | 556 | if (!(parcel_interface is LandObject)) |
543 | float x = (min.X + max.X) / 2; | 557 | continue; |
544 | float y = (min.Y + max.Y) / 2; | 558 | |
545 | mapitem = new mapItemReply( | 559 | LandObject land = (LandObject)parcel_interface; |
546 | xstart + (uint)x, | 560 | LandData parcel = land.LandData; |
547 | ystart + (uint)y, | 561 | |
548 | parcel.GlobalID, | 562 | // Show land for sale |
549 | parcel.Name, | 563 | if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale) |
550 | parcel.Area, | 564 | { |
551 | parcel.SalePrice | 565 | Vector3 min = parcel.AABBMin; |
552 | ); | 566 | Vector3 max = parcel.AABBMax; |
553 | mapitems.Add(mapitem); | 567 | float x = (min.X + max.X) / 2; |
568 | float y = (min.Y + max.Y) / 2; | ||
569 | mapitem = new mapItemReply( | ||
570 | xstart + (uint)x, | ||
571 | ystart + (uint)y, | ||
572 | parcel.GlobalID, | ||
573 | parcel.Name, | ||
574 | parcel.Area, | ||
575 | parcel.SalePrice | ||
576 | ); | ||
577 | mapitems.Add(mapitem); | ||
578 | } | ||
554 | } | 579 | } |
555 | } | 580 | } |
556 | } | 581 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); |
557 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | 582 | break; |
558 | break; | 583 | |
584 | case (uint)GridItemType.PgEvent: | ||
585 | case (uint)GridItemType.MatureEvent: | ||
586 | case (uint)GridItemType.AdultEvent: | ||
587 | case (uint)GridItemType.Classified: | ||
588 | case (uint)GridItemType.Popular: | ||
589 | // TODO | ||
590 | // just dont not cry about them | ||
591 | break; | ||
592 | |||
593 | default: | ||
594 | // unkown map item type | ||
595 | m_log.DebugFormat("[WORLD MAP]: Unknown MapItem type {1}", itemtype); | ||
596 | break; | ||
597 | } | ||
598 | } | ||
599 | else | ||
600 | { | ||
601 | // send all items till we get a better fix | ||
559 | 602 | ||
560 | case (int)GridItemType.Telehub: | 603 | // Service 6 right now (MAP_ITEM_AGENTS_LOCATION; green dots) |
561 | // Service 1 (MAP_ITEM_TELEHUB) | ||
562 | 604 | ||
563 | SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject); | 605 | int tc = Environment.TickCount; |
564 | if (sog != null) | 606 | if (m_scene.GetRootAgentCount() <= 1) |
607 | { | ||
608 | mapitem = new mapItemReply( | ||
609 | xstart + 1, | ||
610 | ystart + 1, | ||
611 | UUID.Zero, | ||
612 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), | ||
613 | 0, 0); | ||
614 | mapitems.Add(mapitem); | ||
615 | } | ||
616 | else | ||
617 | { | ||
618 | m_scene.ForEachRootScenePresence(delegate (ScenePresence sp) | ||
565 | { | 619 | { |
566 | mapitem = new mapItemReply( | 620 | // Don't send a green dot for yourself |
567 | xstart + (uint)sog.AbsolutePosition.X, | 621 | if (sp.UUID != remoteClient.AgentId) |
568 | ystart + (uint)sog.AbsolutePosition.Y, | 622 | { |
569 | UUID.Zero, | 623 | mapitem = new mapItemReply( |
570 | sog.Name, | 624 | xstart + (uint)sp.AbsolutePosition.X, |
571 | 0, // color (not used) | 625 | ystart + (uint)sp.AbsolutePosition.Y, |
572 | 0 // 0 = telehub / 1 = infohub | 626 | UUID.Zero, |
573 | ); | 627 | Util.Md5Hash(m_scene.RegionInfo.RegionName + tc.ToString()), |
574 | mapitems.Add(mapitem); | 628 | 1, 0); |
629 | mapitems.Add(mapitem); | ||
630 | } | ||
631 | }); | ||
632 | } | ||
633 | remoteClient.SendMapItemReply(mapitems.ToArray(), 6, flags); | ||
634 | mapitems.Clear(); | ||
575 | 635 | ||
576 | remoteClient.SendMapItemReply(mapitems.ToArray(), itemtype, flags); | 636 | // Service 1 (MAP_ITEM_TELEHUB) |
577 | } | ||
578 | break; | ||
579 | 637 | ||
580 | case (uint)GridItemType.PgEvent: | 638 | SceneObjectGroup sog = m_scene.GetSceneObjectGroup(m_scene.RegionInfo.RegionSettings.TelehubObject); |
581 | case (uint)GridItemType.MatureEvent: | 639 | if (sog != null) |
582 | case (uint)GridItemType.AdultEvent: | 640 | { |
583 | case (uint)GridItemType.Classified: | 641 | mapitem = new mapItemReply( |
584 | case (uint)GridItemType.Popular: | 642 | xstart + (uint)sog.AbsolutePosition.X, |
585 | // TODO | 643 | ystart + (uint)sog.AbsolutePosition.Y, |
586 | // just dont not cry about them | 644 | UUID.Zero, |
587 | break; | 645 | sog.Name, |
646 | 0, // color (not used) | ||
647 | 0 // 0 = telehub / 1 = infohub | ||
648 | ); | ||
649 | mapitems.Add(mapitem); | ||
650 | remoteClient.SendMapItemReply(mapitems.ToArray(), 1, flags); | ||
651 | mapitems.Clear(); | ||
652 | } | ||
588 | 653 | ||
589 | default: | 654 | // Service 7 (MAP_ITEM_LAND_FOR_SALE) |
590 | // unkown map item type | 655 | |
591 | m_log.DebugFormat("[WORLD MAP]: Unknown MapItem type {1}", itemtype); | 656 | uint its = 7; |
592 | break; | 657 | if (m_scene.RegionInfo.RegionSettings.Maturity == 2) |
658 | its = 10; | ||
659 | |||
660 | // Parcels | ||
661 | ILandChannel landChannel = m_scene.LandChannel; | ||
662 | List<ILandObject> parcels = landChannel.AllParcels(); | ||
663 | |||
664 | if ((parcels != null) && (parcels.Count >= 1)) | ||
665 | { | ||
666 | foreach (ILandObject parcel_interface in parcels) | ||
667 | { | ||
668 | // Play it safe | ||
669 | if (!(parcel_interface is LandObject)) | ||
670 | continue; | ||
671 | |||
672 | LandObject land = (LandObject)parcel_interface; | ||
673 | LandData parcel = land.LandData; | ||
674 | |||
675 | // Show land for sale | ||
676 | if ((parcel.Flags & (uint)ParcelFlags.ForSale) == (uint)ParcelFlags.ForSale) | ||
677 | { | ||
678 | Vector3 min = parcel.AABBMin; | ||
679 | Vector3 max = parcel.AABBMax; | ||
680 | float x = (min.X + max.X) / 2; | ||
681 | float y = (min.Y + max.Y) / 2; | ||
682 | mapitem = new mapItemReply( | ||
683 | xstart + (uint)x, | ||
684 | ystart + (uint)y, | ||
685 | parcel.GlobalID, | ||
686 | parcel.Name, | ||
687 | parcel.Area, | ||
688 | parcel.SalePrice | ||
689 | ); | ||
690 | mapitems.Add(mapitem); | ||
691 | } | ||
692 | } | ||
693 | if(mapitems.Count >0) | ||
694 | remoteClient.SendMapItemReply(mapitems.ToArray(), its, flags); | ||
695 | mapitems.Clear(); | ||
696 | } | ||
593 | } | 697 | } |
594 | } | 698 | } |
595 | 699 | ||
@@ -663,6 +767,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
663 | { | 767 | { |
664 | if(av!=null) | 768 | if(av!=null) |
665 | { | 769 | { |
770 | // this will mainly only send green dots now | ||
666 | if (responseMap.ContainsKey(st.itemtype.ToString())) | 771 | if (responseMap.ContainsKey(st.itemtype.ToString())) |
667 | { | 772 | { |
668 | List<mapItemReply> returnitems = new List<mapItemReply>(); | 773 | List<mapItemReply> returnitems = new List<mapItemReply>(); |
@@ -733,6 +838,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
733 | requests.Enqueue(st); | 838 | requests.Enqueue(st); |
734 | } | 839 | } |
735 | 840 | ||
841 | uint[] itemTypesForcedSend = new uint[] { 6, 1, 7, 10 }; // green dots, infohub, land sells | ||
842 | |||
736 | /// <summary> | 843 | /// <summary> |
737 | /// Does the actual remote mapitem request | 844 | /// Does the actual remote mapitem request |
738 | /// This should be called from an asynchronous thread | 845 | /// This should be called from an asynchronous thread |
@@ -906,7 +1013,6 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
906 | lock (m_cachedRegionMapItemsResponses) | 1013 | lock (m_cachedRegionMapItemsResponses) |
907 | m_cachedRegionMapItemsResponses.AddOrUpdate(regionhandle, responseMap, expireResponsesTime); | 1014 | m_cachedRegionMapItemsResponses.AddOrUpdate(regionhandle, responseMap, expireResponsesTime); |
908 | 1015 | ||
909 | // send answer for this item and client | ||
910 | flags &= 0xffff; | 1016 | flags &= 0xffff; |
911 | 1017 | ||
912 | if (id != UUID.Zero) | 1018 | if (id != UUID.Zero) |
@@ -915,23 +1021,29 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
915 | m_scene.TryGetScenePresence(id, out av); | 1021 | m_scene.TryGetScenePresence(id, out av); |
916 | if (av != null && !av.IsChildAgent && !av.IsDeleted && !av.IsInTransit) | 1022 | if (av != null && !av.IsChildAgent && !av.IsDeleted && !av.IsInTransit) |
917 | { | 1023 | { |
918 | if (responseMap.ContainsKey(itemtype.ToString())) | 1024 | // send all the items or viewers will never ask for them, except green dots |
1025 | foreach (uint itfs in itemTypesForcedSend) | ||
919 | { | 1026 | { |
920 | List<mapItemReply> returnitems = new List<mapItemReply>(); | 1027 | if (responseMap.ContainsKey(itfs.ToString())) |
921 | OSDArray itemarray = (OSDArray)responseMap[itemtype.ToString()]; | ||
922 | for (int i = 0; i < itemarray.Count; i++) | ||
923 | { | 1028 | { |
924 | OSDMap mapitem = (OSDMap)itemarray[i]; | 1029 | List<mapItemReply> returnitems = new List<mapItemReply>(); |
925 | mapItemReply mi = new mapItemReply(); | 1030 | // OSDArray itemarray = (OSDArray)responseMap[itemtype.ToString()]; |
926 | mi.x = (uint)mapitem["X"].AsInteger(); | 1031 | OSDArray itemarray = (OSDArray)responseMap[itfs.ToString()]; |
927 | mi.y = (uint)mapitem["Y"].AsInteger(); | 1032 | for (int i = 0; i < itemarray.Count; i++) |
928 | mi.id = mapitem["ID"].AsUUID(); | 1033 | { |
929 | mi.Extra = mapitem["Extra"].AsInteger(); | 1034 | OSDMap mapitem = (OSDMap)itemarray[i]; |
930 | mi.Extra2 = mapitem["Extra2"].AsInteger(); | 1035 | mapItemReply mi = new mapItemReply(); |
931 | mi.name = mapitem["Name"].AsString(); | 1036 | mi.x = (uint)mapitem["X"].AsInteger(); |
932 | returnitems.Add(mi); | 1037 | mi.y = (uint)mapitem["Y"].AsInteger(); |
1038 | mi.id = mapitem["ID"].AsUUID(); | ||
1039 | mi.Extra = mapitem["Extra"].AsInteger(); | ||
1040 | mi.Extra2 = mapitem["Extra2"].AsInteger(); | ||
1041 | mi.name = mapitem["Name"].AsString(); | ||
1042 | returnitems.Add(mi); | ||
1043 | } | ||
1044 | // av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, flags); | ||
1045 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itfs, flags); | ||
933 | } | 1046 | } |
934 | av.ControllingClient.SendMapItemReply(returnitems.ToArray(), itemtype, flags); | ||
935 | } | 1047 | } |
936 | } | 1048 | } |
937 | } | 1049 | } |
@@ -1064,7 +1176,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1064 | { | 1176 | { |
1065 | if (r == null) | 1177 | if (r == null) |
1066 | { | 1178 | { |
1067 | // block.Access = (byte)SimAccess.Down; | 1179 | // we should not get here ?? |
1180 | // block.Access = (byte)SimAccess.Down; this is for a grid reply on r | ||
1068 | block.Access = (byte)SimAccess.NonExistent; | 1181 | block.Access = (byte)SimAccess.NonExistent; |
1069 | block.MapImageId = UUID.Zero; | 1182 | block.MapImageId = UUID.Zero; |
1070 | return; | 1183 | return; |
@@ -1282,10 +1395,6 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1282 | uint xstart = 0; | 1395 | uint xstart = 0; |
1283 | uint ystart = 0; | 1396 | uint ystart = 0; |
1284 | 1397 | ||
1285 | // create and send back answers about all items active | ||
1286 | // so call region can cache them and answer to the several | ||
1287 | // individual item viewer requests | ||
1288 | |||
1289 | Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart); | 1398 | Util.RegionHandleToWorldLoc(m_scene.RegionInfo.RegionHandle, out xstart, out ystart); |
1290 | 1399 | ||
1291 | // Service 6 (MAP_ITEM_AGENTS_LOCATION; green dots) | 1400 | // Service 6 (MAP_ITEM_AGENTS_LOCATION; green dots) |
@@ -1435,7 +1544,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1435 | needRegionSave = true; | 1544 | needRegionSave = true; |
1436 | } | 1545 | } |
1437 | 1546 | ||
1438 | // bypass terrain image for large regions since only V2 viewers work with them | 1547 | // bypass terrain image for large regions |
1439 | if (m_scene.RegionInfo.RegionSizeX <= Constants.RegionSize && | 1548 | if (m_scene.RegionInfo.RegionSizeX <= Constants.RegionSize && |
1440 | m_scene.RegionInfo.RegionSizeY <= Constants.RegionSize | 1549 | m_scene.RegionInfo.RegionSizeY <= Constants.RegionSize |
1441 | && mapbmp != null) | 1550 | && mapbmp != null) |
@@ -1601,8 +1710,8 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
1601 | using (SolidBrush transparent = new SolidBrush(background)) | 1710 | using (SolidBrush transparent = new SolidBrush(background)) |
1602 | g.FillRectangle(transparent, 0, 0, regionSizeX, regionSizeY); | 1711 | g.FillRectangle(transparent, 0, 0, regionSizeX, regionSizeY); |
1603 | 1712 | ||
1604 | // make it a half transparent | 1713 | // make it a bit transparent |
1605 | using (SolidBrush yellow = new SolidBrush(Color.FromArgb(100, 249, 223, 9))) | 1714 | using (SolidBrush yellow = new SolidBrush(Color.FromArgb(192, 249, 223, 9))) |
1606 | { | 1715 | { |
1607 | for (int x = 0; x < regionLandTilesX; x++) | 1716 | for (int x = 0; x < regionLandTilesX; x++) |
1608 | { | 1717 | { |