diff options
Diffstat (limited to 'OpenSim/Region/CoreModules/World')
-rw-r--r-- | OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs | 148 |
1 files changed, 97 insertions, 51 deletions
diff --git a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs index 08891d9..3fda67f 100644 --- a/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs +++ b/OpenSim/Region/CoreModules/World/Terrain/TerrainModule.cs | |||
@@ -149,8 +149,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
149 | 149 | ||
150 | m_scene.RegisterModuleInterface<ITerrainModule>(this); | 150 | m_scene.RegisterModuleInterface<ITerrainModule>(this); |
151 | m_scene.EventManager.OnNewClient += EventManager_OnNewClient; | 151 | m_scene.EventManager.OnNewClient += EventManager_OnNewClient; |
152 | m_scene.EventManager.OnClientClosed += EventManager_OnClientClosed; | ||
152 | m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; | 153 | m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; |
153 | m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick; | 154 | m_scene.EventManager.OnTerrainTick += EventManager_OnTerrainTick; |
155 | m_scene.EventManager.OnFrame += EventManager_OnFrame; | ||
154 | } | 156 | } |
155 | 157 | ||
156 | InstallDefaultEffects(); | 158 | InstallDefaultEffects(); |
@@ -189,8 +191,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
189 | // remove the commands | 191 | // remove the commands |
190 | m_scene.UnregisterModuleCommander(m_commander.Name); | 192 | m_scene.UnregisterModuleCommander(m_commander.Name); |
191 | // remove the event-handlers | 193 | // remove the event-handlers |
194 | m_scene.EventManager.OnFrame -= EventManager_OnFrame; | ||
192 | m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick; | 195 | m_scene.EventManager.OnTerrainTick -= EventManager_OnTerrainTick; |
193 | m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole; | 196 | m_scene.EventManager.OnPluginConsole -= EventManager_OnPluginConsole; |
197 | m_scene.EventManager.OnClientClosed -= EventManager_OnClientClosed; | ||
194 | m_scene.EventManager.OnNewClient -= EventManager_OnNewClient; | 198 | m_scene.EventManager.OnNewClient -= EventManager_OnNewClient; |
195 | // remove the interface | 199 | // remove the interface |
196 | m_scene.UnregisterModuleInterface<ITerrainModule>(this); | 200 | m_scene.UnregisterModuleInterface<ITerrainModule>(this); |
@@ -266,7 +270,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
266 | String.Format("Unable to load heightmap: {0}", e.Message)); | 270 | String.Format("Unable to load heightmap: {0}", e.Message)); |
267 | } | 271 | } |
268 | } | 272 | } |
269 | CheckForTerrainUpdates(); | ||
270 | m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); | 273 | m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); |
271 | return; | 274 | return; |
272 | } | 275 | } |
@@ -347,7 +350,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
347 | } | 350 | } |
348 | } | 351 | } |
349 | 352 | ||
350 | CheckForTerrainUpdates(); | ||
351 | m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); | 353 | m_log.Info("[TERRAIN]: File (" + filename + ") loaded successfully"); |
352 | return; | 354 | return; |
353 | } | 355 | } |
@@ -418,7 +420,7 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
418 | 420 | ||
419 | public void TaintTerrain () | 421 | public void TaintTerrain () |
420 | { | 422 | { |
421 | CheckForTerrainUpdates(); | 423 | m_channel.GetTerrainData().TaintAllTerrain(); |
422 | } | 424 | } |
423 | 425 | ||
424 | #region Plugin Loading Methods | 426 | #region Plugin Loading Methods |
@@ -647,7 +649,39 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
647 | } | 649 | } |
648 | 650 | ||
649 | /// <summary> | 651 | /// <summary> |
652 | /// Called before processing of every simulation frame. | ||
653 | /// This is used to check to see of any of the terrain is tainted and, if so, schedule | ||
654 | /// updates for all the presences. | ||
655 | /// This also checks to see if there are updates that need to be sent for each presence. | ||
656 | /// This is where the logic is to send terrain updates to clients. | ||
657 | /// </summary> | ||
658 | private void EventManager_OnFrame() | ||
659 | { | ||
660 | TerrainData terrData = m_channel.GetTerrainData(); | ||
661 | |||
662 | bool shouldTaint = false; | ||
663 | for (int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize) | ||
664 | { | ||
665 | for (int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize) | ||
666 | { | ||
667 | if (terrData.IsTaintedAt(x, y)) | ||
668 | { | ||
669 | // Found a patch that was modified. Push this flag into the clients. | ||
670 | SendToClients(terrData, x, y); | ||
671 | shouldTaint = true; | ||
672 | } | ||
673 | } | ||
674 | } | ||
675 | if (shouldTaint) | ||
676 | { | ||
677 | m_scene.EventManager.TriggerTerrainTainted(); | ||
678 | m_tainted = true; | ||
679 | } | ||
680 | } | ||
681 | |||
682 | /// <summary> | ||
650 | /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections | 683 | /// Performs updates to the region periodically, synchronising physics and other heightmap aware sections |
684 | /// Called infrequently (like every 5 seconds or so). Best used for storing terrain. | ||
651 | /// </summary> | 685 | /// </summary> |
652 | private void EventManager_OnTerrainTick() | 686 | private void EventManager_OnTerrainTick() |
653 | { | 687 | { |
@@ -698,6 +732,22 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
698 | } | 732 | } |
699 | 733 | ||
700 | /// <summary> | 734 | /// <summary> |
735 | /// Installs terrain brush hook to IClientAPI | ||
736 | /// </summary> | ||
737 | /// <param name="client"></param> | ||
738 | private void EventManager_OnClientClosed(UUID client, Scene scene) | ||
739 | { | ||
740 | ScenePresence presence = scene.GetScenePresence(client); | ||
741 | if (presence != null) | ||
742 | { | ||
743 | presence.ControllingClient.OnModifyTerrain -= client_OnModifyTerrain; | ||
744 | presence.ControllingClient.OnBakeTerrain -= client_OnBakeTerrain; | ||
745 | presence.ControllingClient.OnLandUndo -= client_OnLandUndo; | ||
746 | presence.ControllingClient.OnUnackedTerrain -= client_OnUnackedTerrain; | ||
747 | } | ||
748 | } | ||
749 | |||
750 | /// <summary> | ||
701 | /// Checks to see if the terrain has been modified since last check | 751 | /// Checks to see if the terrain has been modified since last check |
702 | /// but won't attempt to limit those changes to the limits specified in the estate settings | 752 | /// but won't attempt to limit those changes to the limits specified in the estate settings |
703 | /// currently invoked by the command line operations in the region server only | 753 | /// currently invoked by the command line operations in the region server only |
@@ -717,36 +767,32 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
717 | /// </summary> | 767 | /// </summary> |
718 | private void CheckForTerrainUpdates(bool respectEstateSettings) | 768 | private void CheckForTerrainUpdates(bool respectEstateSettings) |
719 | { | 769 | { |
720 | bool shouldTaint = false; | 770 | } |
721 | float[] terrHeights = m_channel.GetFloatsSerialised(); | 771 | |
722 | int x; | 772 | /// <summary> |
723 | for (x = 0; x < m_channel.Width; x += Constants.TerrainPatchSize) | 773 | /// Scan over changes in the terrain and limit height changes. This enforces the |
774 | /// non-estate owner limits on rate of terrain editting. | ||
775 | /// Returns 'true' if any heights were limited. | ||
776 | /// </summary> | ||
777 | private bool EnforceEstateLimits() | ||
778 | { | ||
779 | TerrainData terrData = m_channel.GetTerrainData(); | ||
780 | |||
781 | bool wasLimited = false; | ||
782 | for (int x = 0; x < terrData.SizeX; x += Constants.TerrainPatchSize) | ||
724 | { | 783 | { |
725 | int y; | 784 | for (int y = 0; y < terrData.SizeY; y += Constants.TerrainPatchSize) |
726 | for (y = 0; y < m_channel.Height; y += Constants.TerrainPatchSize) | ||
727 | { | 785 | { |
728 | if (m_channel.Tainted(x, y)) | 786 | if (terrData.IsTaintedAt(x, y, false /* clearOnTest */)) |
729 | { | 787 | { |
730 | // If we should respect the estate settings then | 788 | // If we should respect the estate settings then |
731 | // fixup and height deltas that don't respect them. | 789 | // fixup and height deltas that don't respect them. |
732 | // Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values. | 790 | // Note that LimitChannelChanges() modifies the TerrainChannel with the limited height values. |
733 | if (respectEstateSettings && LimitChannelChanges(x, y)) | 791 | wasLimited |= LimitChannelChanges(terrData, x, y); |
734 | { | ||
735 | // Terrain heights were modified. Refetch the terrain info. | ||
736 | terrHeights = m_channel.GetFloatsSerialised(); | ||
737 | } | ||
738 | |||
739 | // m_log.DebugFormat("{0} Patch modified. Sending (x,y) = ({1},{2})", LogHeader, x, y); | ||
740 | SendToClients(terrHeights, x, y); | ||
741 | shouldTaint = true; | ||
742 | } | 792 | } |
743 | } | 793 | } |
744 | } | 794 | } |
745 | if (shouldTaint) | 795 | return wasLimited; |
746 | { | ||
747 | m_scene.EventManager.TriggerTerrainTainted(); | ||
748 | m_tainted = true; | ||
749 | } | ||
750 | } | 796 | } |
751 | 797 | ||
752 | /// <summary> | 798 | /// <summary> |
@@ -754,11 +800,11 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
754 | /// are all within the current estate limits | 800 | /// are all within the current estate limits |
755 | /// <returns>true if changes were limited, false otherwise</returns> | 801 | /// <returns>true if changes were limited, false otherwise</returns> |
756 | /// </summary> | 802 | /// </summary> |
757 | private bool LimitChannelChanges(int xStart, int yStart) | 803 | private bool LimitChannelChanges(TerrainData terrData, int xStart, int yStart) |
758 | { | 804 | { |
759 | bool changesLimited = false; | 805 | bool changesLimited = false; |
760 | double minDelta = m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; | 806 | float minDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainLowerLimit; |
761 | double maxDelta = m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; | 807 | float maxDelta = (float)m_scene.RegionInfo.RegionSettings.TerrainRaiseLimit; |
762 | 808 | ||
763 | // loop through the height map for this patch and compare it against | 809 | // loop through the height map for this patch and compare it against |
764 | // the revert map | 810 | // the revert map |
@@ -766,19 +812,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
766 | { | 812 | { |
767 | for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) | 813 | for (int y = yStart; y < yStart + Constants.TerrainPatchSize; y++) |
768 | { | 814 | { |
769 | 815 | float requestedHeight = terrData[x, y]; | |
770 | double requestedHeight = m_channel[x, y]; | 816 | float bakedHeight = (float)m_revert[x, y]; |
771 | double bakedHeight = m_revert[x, y]; | 817 | float requestedDelta = requestedHeight - bakedHeight; |
772 | double requestedDelta = requestedHeight - bakedHeight; | ||
773 | 818 | ||
774 | if (requestedDelta > maxDelta) | 819 | if (requestedDelta > maxDelta) |
775 | { | 820 | { |
776 | m_channel[x, y] = bakedHeight + maxDelta; | 821 | terrData[x, y] = bakedHeight + maxDelta; |
777 | changesLimited = true; | 822 | changesLimited = true; |
778 | } | 823 | } |
779 | else if (requestedDelta < minDelta) | 824 | else if (requestedDelta < minDelta) |
780 | { | 825 | { |
781 | m_channel[x, y] = bakedHeight + minDelta; //as lower is a -ve delta | 826 | terrData[x, y] = bakedHeight + minDelta; //as lower is a -ve delta |
782 | changesLimited = true; | 827 | changesLimited = true; |
783 | } | 828 | } |
784 | } | 829 | } |
@@ -806,11 +851,18 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
806 | /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param> | 851 | /// <param name="serialised">A copy of the terrain as a 1D float array of size w*h</param> |
807 | /// <param name="x">The patch corner to send</param> | 852 | /// <param name="x">The patch corner to send</param> |
808 | /// <param name="y">The patch corner to send</param> | 853 | /// <param name="y">The patch corner to send</param> |
809 | private void SendToClients(float[] heightMap, int x, int y) | 854 | private void SendToClients(TerrainData terrData, int x, int y) |
810 | { | 855 | { |
856 | // We know the actual terrain data passed is ignored. This kludge saves changing IClientAPI. | ||
857 | //float[] heightMap = terrData.GetFloatsSerialized(); | ||
858 | float[] heightMap = new float[10]; | ||
811 | m_scene.ForEachClient( | 859 | m_scene.ForEachClient( |
812 | delegate(IClientAPI controller) | 860 | delegate(IClientAPI controller) |
813 | { controller.SendLayerData( x / Constants.TerrainPatchSize, y / Constants.TerrainPatchSize, heightMap); } | 861 | { |
862 | controller.SendLayerData( x / Constants.TerrainPatchSize, | ||
863 | y / Constants.TerrainPatchSize, | ||
864 | heightMap); | ||
865 | } | ||
814 | ); | 866 | ); |
815 | } | 867 | } |
816 | 868 | ||
@@ -856,7 +908,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
856 | m_painteffects[(StandardTerrainEffects) action].PaintEffect( | 908 | m_painteffects[(StandardTerrainEffects) action].PaintEffect( |
857 | m_channel, allowMask, west, south, height, size, seconds); | 909 | m_channel, allowMask, west, south, height, size, seconds); |
858 | 910 | ||
859 | CheckForTerrainUpdates(!god); //revert changes outside estate limits | 911 | //revert changes outside estate limits |
912 | if (!god) | ||
913 | EnforceEstateLimits(); | ||
860 | } | 914 | } |
861 | } | 915 | } |
862 | else | 916 | else |
@@ -897,7 +951,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
897 | m_floodeffects[(StandardTerrainEffects) action].FloodEffect( | 951 | m_floodeffects[(StandardTerrainEffects) action].FloodEffect( |
898 | m_channel, fillArea, size); | 952 | m_channel, fillArea, size); |
899 | 953 | ||
900 | CheckForTerrainUpdates(!god); //revert changes outside estate limits | 954 | //revert changes outside estate limits |
955 | if (!god) | ||
956 | EnforceEstateLimits(); | ||
901 | } | 957 | } |
902 | } | 958 | } |
903 | else | 959 | else |
@@ -921,7 +977,9 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
921 | protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY) | 977 | protected void client_OnUnackedTerrain(IClientAPI client, int patchX, int patchY) |
922 | { | 978 | { |
923 | //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY); | 979 | //m_log.Debug("Terrain packet unacked, resending patch: " + patchX + " , " + patchY); |
924 | client.SendLayerData(patchX, patchY, m_scene.Heightmap.GetFloatsSerialised()); | 980 | // SendLayerData does not use the heightmap parameter. This kludge is so as to not change IClientAPI. |
981 | float[] heightMap = new float[10]; | ||
982 | client.SendLayerData(patchX, patchY, heightMap); | ||
925 | } | 983 | } |
926 | 984 | ||
927 | private void StoreUndoState() | 985 | private void StoreUndoState() |
@@ -948,7 +1006,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
948 | private void InterfaceLoadFile(Object[] args) | 1006 | private void InterfaceLoadFile(Object[] args) |
949 | { | 1007 | { |
950 | LoadFromFile((string) args[0]); | 1008 | LoadFromFile((string) args[0]); |
951 | CheckForTerrainUpdates(); | ||
952 | } | 1009 | } |
953 | 1010 | ||
954 | private void InterfaceLoadTileFile(Object[] args) | 1011 | private void InterfaceLoadTileFile(Object[] args) |
@@ -958,7 +1015,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
958 | (int) args[2], | 1015 | (int) args[2], |
959 | (int) args[3], | 1016 | (int) args[3], |
960 | (int) args[4]); | 1017 | (int) args[4]); |
961 | CheckForTerrainUpdates(); | ||
962 | } | 1018 | } |
963 | 1019 | ||
964 | private void InterfaceSaveFile(Object[] args) | 1020 | private void InterfaceSaveFile(Object[] args) |
@@ -987,7 +1043,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
987 | for (y = 0; y < m_channel.Height; y++) | 1043 | for (y = 0; y < m_channel.Height; y++) |
988 | m_channel[x, y] = m_revert[x, y]; | 1044 | m_channel[x, y] = m_revert[x, y]; |
989 | 1045 | ||
990 | CheckForTerrainUpdates(); | ||
991 | } | 1046 | } |
992 | 1047 | ||
993 | private void InterfaceFlipTerrain(Object[] args) | 1048 | private void InterfaceFlipTerrain(Object[] args) |
@@ -1028,7 +1083,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1028 | } | 1083 | } |
1029 | 1084 | ||
1030 | 1085 | ||
1031 | CheckForTerrainUpdates(); | ||
1032 | } | 1086 | } |
1033 | 1087 | ||
1034 | private void InterfaceRescaleTerrain(Object[] args) | 1088 | private void InterfaceRescaleTerrain(Object[] args) |
@@ -1086,7 +1140,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1086 | } | 1140 | } |
1087 | } | 1141 | } |
1088 | 1142 | ||
1089 | CheckForTerrainUpdates(); | ||
1090 | } | 1143 | } |
1091 | 1144 | ||
1092 | } | 1145 | } |
@@ -1097,7 +1150,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1097 | for (x = 0; x < m_channel.Width; x++) | 1150 | for (x = 0; x < m_channel.Width; x++) |
1098 | for (y = 0; y < m_channel.Height; y++) | 1151 | for (y = 0; y < m_channel.Height; y++) |
1099 | m_channel[x, y] += (double) args[0]; | 1152 | m_channel[x, y] += (double) args[0]; |
1100 | CheckForTerrainUpdates(); | ||
1101 | } | 1153 | } |
1102 | 1154 | ||
1103 | private void InterfaceMultiplyTerrain(Object[] args) | 1155 | private void InterfaceMultiplyTerrain(Object[] args) |
@@ -1106,7 +1158,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1106 | for (x = 0; x < m_channel.Width; x++) | 1158 | for (x = 0; x < m_channel.Width; x++) |
1107 | for (y = 0; y < m_channel.Height; y++) | 1159 | for (y = 0; y < m_channel.Height; y++) |
1108 | m_channel[x, y] *= (double) args[0]; | 1160 | m_channel[x, y] *= (double) args[0]; |
1109 | CheckForTerrainUpdates(); | ||
1110 | } | 1161 | } |
1111 | 1162 | ||
1112 | private void InterfaceLowerTerrain(Object[] args) | 1163 | private void InterfaceLowerTerrain(Object[] args) |
@@ -1115,7 +1166,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1115 | for (x = 0; x < m_channel.Width; x++) | 1166 | for (x = 0; x < m_channel.Width; x++) |
1116 | for (y = 0; y < m_channel.Height; y++) | 1167 | for (y = 0; y < m_channel.Height; y++) |
1117 | m_channel[x, y] -= (double) args[0]; | 1168 | m_channel[x, y] -= (double) args[0]; |
1118 | CheckForTerrainUpdates(); | ||
1119 | } | 1169 | } |
1120 | 1170 | ||
1121 | private void InterfaceFillTerrain(Object[] args) | 1171 | private void InterfaceFillTerrain(Object[] args) |
@@ -1125,7 +1175,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1125 | for (x = 0; x < m_channel.Width; x++) | 1175 | for (x = 0; x < m_channel.Width; x++) |
1126 | for (y = 0; y < m_channel.Height; y++) | 1176 | for (y = 0; y < m_channel.Height; y++) |
1127 | m_channel[x, y] = (double) args[0]; | 1177 | m_channel[x, y] = (double) args[0]; |
1128 | CheckForTerrainUpdates(); | ||
1129 | } | 1178 | } |
1130 | 1179 | ||
1131 | private void InterfaceMinTerrain(Object[] args) | 1180 | private void InterfaceMinTerrain(Object[] args) |
@@ -1138,7 +1187,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1138 | m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]); | 1187 | m_channel[x, y] = Math.Max((double)args[0], m_channel[x, y]); |
1139 | } | 1188 | } |
1140 | } | 1189 | } |
1141 | CheckForTerrainUpdates(); | ||
1142 | } | 1190 | } |
1143 | 1191 | ||
1144 | private void InterfaceMaxTerrain(Object[] args) | 1192 | private void InterfaceMaxTerrain(Object[] args) |
@@ -1151,7 +1199,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1151 | m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]); | 1199 | m_channel[x, y] = Math.Min((double)args[0], m_channel[x, y]); |
1152 | } | 1200 | } |
1153 | } | 1201 | } |
1154 | CheckForTerrainUpdates(); | ||
1155 | } | 1202 | } |
1156 | 1203 | ||
1157 | private void InterfaceShowDebugStats(Object[] args) | 1204 | private void InterfaceShowDebugStats(Object[] args) |
@@ -1214,7 +1261,6 @@ namespace OpenSim.Region.CoreModules.World.Terrain | |||
1214 | if (m_plugineffects.ContainsKey(firstArg)) | 1261 | if (m_plugineffects.ContainsKey(firstArg)) |
1215 | { | 1262 | { |
1216 | m_plugineffects[firstArg].RunEffect(m_channel); | 1263 | m_plugineffects[firstArg].RunEffect(m_channel); |
1217 | CheckForTerrainUpdates(); | ||
1218 | } | 1264 | } |
1219 | else | 1265 | else |
1220 | { | 1266 | { |