diff options
10 files changed, 107 insertions, 68 deletions
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs index e5cefe4..8bf2bf8 100644 --- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs +++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs | |||
@@ -1203,6 +1203,8 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer | |||
1203 | 1203 | ||
1204 | m_log.Debug("[ENTITY TRANSFER MODULE]: Completed inform client about neighbour " + endPoint.ToString()); | 1204 | m_log.Debug("[ENTITY TRANSFER MODULE]: Completed inform client about neighbour " + endPoint.ToString()); |
1205 | } | 1205 | } |
1206 | if (!regionAccepted) | ||
1207 | m_log.DebugFormat("[ENTITY TRANSFER MODULE]: Region {0} did not accept agent: {1}", reg.RegionName, reason); | ||
1206 | } | 1208 | } |
1207 | 1209 | ||
1208 | /// <summary> | 1210 | /// <summary> |
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs index 37b403e..2dd0099 100644 --- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs +++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Simulation/LocalSimulationConnector.cs | |||
@@ -300,7 +300,10 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation | |||
300 | if (s.RegionInfo.RegionID == destination.RegionID) | 300 | if (s.RegionInfo.RegionID == destination.RegionID) |
301 | { | 301 | { |
302 | //m_log.Debug("[LOCAL COMMS]: Found region to SendCloseAgent"); | 302 | //m_log.Debug("[LOCAL COMMS]: Found region to SendCloseAgent"); |
303 | return s.IncomingCloseAgent(id); | 303 | // Let's spawn a threadlet right here, because this may take |
304 | // a while | ||
305 | Util.FireAndForget(delegate { s.IncomingCloseAgent(id); }); | ||
306 | return true; | ||
304 | } | 307 | } |
305 | } | 308 | } |
306 | //m_log.Debug("[LOCAL COMMS]: region not found in SendCloseAgent"); | 309 | //m_log.Debug("[LOCAL COMMS]: region not found in SendCloseAgent"); |
diff --git a/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs b/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs index bdd1a0b..a9e46d0 100644 --- a/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs +++ b/OpenSim/Region/CoreModules/World/WorldMap/MapSearchModule.cs | |||
@@ -102,7 +102,7 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
102 | GridRegion info = m_scene.GridService.GetRegionByName(m_scene.RegionInfo.ScopeID, mapName); | 102 | GridRegion info = m_scene.GridService.GetRegionByName(m_scene.RegionInfo.ScopeID, mapName); |
103 | if (info != null) regionInfos.Add(info); | 103 | if (info != null) regionInfos.Add(info); |
104 | } | 104 | } |
105 | 105 | m_log.DebugFormat("[MAPSEARCHMODULE]: search {0} returned {1} regions", mapName, regionInfos.Count); | |
106 | List<MapBlockData> blocks = new List<MapBlockData>(); | 106 | List<MapBlockData> blocks = new List<MapBlockData>(); |
107 | 107 | ||
108 | MapBlockData data; | 108 | MapBlockData data; |
@@ -128,14 +128,14 @@ namespace OpenSim.Region.CoreModules.World.WorldMap | |||
128 | data.Agents = 0; | 128 | data.Agents = 0; |
129 | data.Access = 255; | 129 | data.Access = 255; |
130 | data.MapImageId = UUID.Zero; | 130 | data.MapImageId = UUID.Zero; |
131 | data.Name = mapName; | 131 | data.Name = ""; // mapName; |
132 | data.RegionFlags = 0; | 132 | data.RegionFlags = 0; |
133 | data.WaterHeight = 0; // not used | 133 | data.WaterHeight = 0; // not used |
134 | data.X = 0; | 134 | data.X = 0; |
135 | data.Y = 0; | 135 | data.Y = 0; |
136 | blocks.Add(data); | 136 | blocks.Add(data); |
137 | 137 | ||
138 | remoteClient.SendMapBlock(blocks, 2); | 138 | remoteClient.SendMapBlock(blocks, 0); |
139 | } | 139 | } |
140 | 140 | ||
141 | // private Scene GetClientScene(IClientAPI client) | 141 | // private Scene GetClientScene(IClientAPI client) |
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index e27b2ba..12fd813 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs | |||
@@ -2592,12 +2592,19 @@ namespace OpenSim.Region.Framework.Scenes | |||
2592 | // and the scene presence and the client, if they exist | 2592 | // and the scene presence and the client, if they exist |
2593 | try | 2593 | try |
2594 | { | 2594 | { |
2595 | ScenePresence sp = GetScenePresence(agentID); | 2595 | // We need to wait for the client to make UDP contact first. |
2596 | PresenceService.LogoutAgent(sp.ControllingClient.SessionId); | 2596 | // It's the UDP contact that creates the scene presence |
2597 | 2597 | ScenePresence sp = WaitGetScenePresence(agentID); | |
2598 | if (sp != null) | 2598 | if (sp != null) |
2599 | sp.ControllingClient.Close(); | 2599 | { |
2600 | PresenceService.LogoutAgent(sp.ControllingClient.SessionId); | ||
2600 | 2601 | ||
2602 | sp.ControllingClient.Close(); | ||
2603 | } | ||
2604 | else | ||
2605 | { | ||
2606 | m_log.WarnFormat("[SCENE]: Could not find scene presence for {0}", agentID); | ||
2607 | } | ||
2601 | // BANG! SLASH! | 2608 | // BANG! SLASH! |
2602 | m_authenticateHandler.RemoveCircuit(agentID); | 2609 | m_authenticateHandler.RemoveCircuit(agentID); |
2603 | 2610 | ||
diff --git a/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs index 69ebe9e..f8ff308 100644 --- a/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs +++ b/OpenSim/Region/Framework/Scenes/SceneCommunicationService.cs | |||
@@ -258,13 +258,17 @@ namespace OpenSim.Region.Framework.Scenes | |||
258 | 258 | ||
259 | } | 259 | } |
260 | 260 | ||
261 | public delegate void SendCloseChildAgentDelegate(UUID agentID, ulong regionHandle); | 261 | //public delegate void SendCloseChildAgentDelegate(UUID agentID, ulong regionHandle); |
262 | //private void SendCloseChildAgentCompleted(IAsyncResult iar) | ||
263 | //{ | ||
264 | // SendCloseChildAgentDelegate icon = (SendCloseChildAgentDelegate)iar.AsyncState; | ||
265 | // icon.EndInvoke(iar); | ||
266 | //} | ||
262 | 267 | ||
263 | /// <summary> | 268 | /// <summary> |
264 | /// This Closes child agents on neighboring regions | 269 | /// Closes a child agent on a given region |
265 | /// Calls an asynchronous method to do so.. so it doesn't lag the sim. | ||
266 | /// </summary> | 270 | /// </summary> |
267 | protected void SendCloseChildAgentAsync(UUID agentID, ulong regionHandle) | 271 | protected void SendCloseChildAgent(UUID agentID, ulong regionHandle) |
268 | { | 272 | { |
269 | 273 | ||
270 | m_log.Debug("[INTERGRID]: Sending close agent to " + regionHandle); | 274 | m_log.Debug("[INTERGRID]: Sending close agent to " + regionHandle); |
@@ -277,21 +281,21 @@ namespace OpenSim.Region.Framework.Scenes | |||
277 | m_scene.SimulationService.CloseAgent(destination, agentID); | 281 | m_scene.SimulationService.CloseAgent(destination, agentID); |
278 | } | 282 | } |
279 | 283 | ||
280 | private void SendCloseChildAgentCompleted(IAsyncResult iar) | 284 | /// <summary> |
281 | { | 285 | /// Closes a child agents in a collection of regions. Does so asynchronously |
282 | SendCloseChildAgentDelegate icon = (SendCloseChildAgentDelegate)iar.AsyncState; | 286 | /// so that the caller doesn't wait. |
283 | icon.EndInvoke(iar); | 287 | /// </summary> |
284 | } | 288 | /// <param name="agentID"></param> |
285 | 289 | /// <param name="regionslst"></param> | |
286 | public void SendCloseChildAgentConnections(UUID agentID, List<ulong> regionslst) | 290 | public void SendCloseChildAgentConnections(UUID agentID, List<ulong> regionslst) |
287 | { | 291 | { |
288 | foreach (ulong handle in regionslst) | 292 | Util.FireAndForget(delegate |
289 | { | 293 | { |
290 | SendCloseChildAgentDelegate d = SendCloseChildAgentAsync; | 294 | foreach (ulong handle in regionslst) |
291 | d.BeginInvoke(agentID, handle, | 295 | { |
292 | SendCloseChildAgentCompleted, | 296 | SendCloseChildAgent(agentID, handle); |
293 | d); | 297 | } |
294 | } | 298 | }); |
295 | } | 299 | } |
296 | 300 | ||
297 | public List<GridRegion> RequestNamedRegions(string name, int maxNumber) | 301 | public List<GridRegion> RequestNamedRegions(string name, int maxNumber) |
diff --git a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs index 3978a7d..83906d7 100644 --- a/OpenSim/Region/Framework/Scenes/UuidGatherer.cs +++ b/OpenSim/Region/Framework/Scenes/UuidGatherer.cs | |||
@@ -86,6 +86,10 @@ namespace OpenSim.Region.Framework.Scenes | |||
86 | /// <param name="assetUuids">The assets gathered</param> | 86 | /// <param name="assetUuids">The assets gathered</param> |
87 | public void GatherAssetUuids(UUID assetUuid, AssetType assetType, IDictionary<UUID, AssetType> assetUuids) | 87 | public void GatherAssetUuids(UUID assetUuid, AssetType assetType, IDictionary<UUID, AssetType> assetUuids) |
88 | { | 88 | { |
89 | // avoid infinite loops | ||
90 | if (assetUuids.ContainsKey(assetUuid)) | ||
91 | return; | ||
92 | |||
89 | try | 93 | try |
90 | { | 94 | { |
91 | assetUuids[assetUuid] = assetType; | 95 | assetUuids[assetUuid] = assetType; |
diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs index 8e712b6..783791f 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs | |||
@@ -864,7 +864,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance | |||
864 | 864 | ||
865 | public Dictionary<string, object> GetVars() | 865 | public Dictionary<string, object> GetVars() |
866 | { | 866 | { |
867 | return m_Script.GetVars(); | 867 | if (m_Script != null) |
868 | return m_Script.GetVars(); | ||
869 | else | ||
870 | return new Dictionary<string, object>(); | ||
868 | } | 871 | } |
869 | 872 | ||
870 | public void SetVars(Dictionary<string, object> vars) | 873 | public void SetVars(Dictionary<string, object> vars) |
diff --git a/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs b/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs index 1aa3282..8ab323a 100644 --- a/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs +++ b/OpenSim/Services/Connectors/Hypergrid/GatekeeperServiceConnector.cs | |||
@@ -158,10 +158,17 @@ namespace OpenSim.Services.Connectors.Hypergrid | |||
158 | try | 158 | try |
159 | { | 159 | { |
160 | WebClient c = new WebClient(); | 160 | WebClient c = new WebClient(); |
161 | //m_log.Debug("JPEG: " + imageURL); | ||
162 | string name = regionID.ToString(); | 161 | string name = regionID.ToString(); |
163 | filename = Path.Combine(storagePath, name + ".jpg"); | 162 | filename = Path.Combine(storagePath, name + ".jpg"); |
164 | c.DownloadFile(imageURL, filename); | 163 | m_log.DebugFormat("[GATEKEEPER SERVICE CONNECTOR]: Map image at {0}, cached at {1}", imageURL, filename); |
164 | if (!File.Exists(filename)) | ||
165 | { | ||
166 | m_log.DebugFormat("[GATEKEEPER SERVICE CONNECTOR]: downloading..."); | ||
167 | c.DownloadFile(imageURL, filename); | ||
168 | } | ||
169 | else | ||
170 | m_log.DebugFormat("[GATEKEEPER SERVICE CONNECTOR]: using cached image"); | ||
171 | |||
165 | bitmap = new Bitmap(filename); | 172 | bitmap = new Bitmap(filename); |
166 | //m_log.Debug("Size: " + m.PhysicalDimension.Height + "-" + m.PhysicalDimension.Width); | 173 | //m_log.Debug("Size: " + m.PhysicalDimension.Height + "-" + m.PhysicalDimension.Width); |
167 | byte[] imageData = OpenJPEG.EncodeFromImage(bitmap, true); | 174 | byte[] imageData = OpenJPEG.EncodeFromImage(bitmap, true); |
@@ -172,10 +179,11 @@ namespace OpenSim.Services.Connectors.Hypergrid | |||
172 | 179 | ||
173 | ass.Data = imageData; | 180 | ass.Data = imageData; |
174 | 181 | ||
175 | m_AssetService.Store(ass); | 182 | mapTile = ass.FullID; |
176 | 183 | ||
177 | // finally | 184 | // finally |
178 | mapTile = ass.FullID; | 185 | m_AssetService.Store(ass); |
186 | |||
179 | } | 187 | } |
180 | catch // LEGIT: Catching problems caused by OpenJPEG p/invoke | 188 | catch // LEGIT: Catching problems caused by OpenJPEG p/invoke |
181 | { | 189 | { |
diff --git a/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs b/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs index 7ddcfa6..c40a347 100644 --- a/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs +++ b/OpenSim/Services/Connectors/Hypergrid/UserAgentServiceConnector.cs | |||
@@ -60,7 +60,9 @@ namespace OpenSim.Services.Connectors.Hypergrid | |||
60 | { | 60 | { |
61 | Uri m_Uri = new Uri(m_ServerURL); | 61 | Uri m_Uri = new Uri(m_ServerURL); |
62 | IPAddress ip = Util.GetHostFromDNS(m_Uri.Host); | 62 | IPAddress ip = Util.GetHostFromDNS(m_Uri.Host); |
63 | m_ServerURL = m_ServerURL.Replace(m_Uri.Host, ip.ToString()); ; | 63 | m_ServerURL = m_ServerURL.Replace(m_Uri.Host, ip.ToString()); |
64 | if (!m_ServerURL.EndsWith("/")) | ||
65 | m_ServerURL += "/"; | ||
64 | } | 66 | } |
65 | catch (Exception e) | 67 | catch (Exception e) |
66 | { | 68 | { |
@@ -87,6 +89,8 @@ namespace OpenSim.Services.Connectors.Hypergrid | |||
87 | throw new Exception("UserAgent connector init error"); | 89 | throw new Exception("UserAgent connector init error"); |
88 | } | 90 | } |
89 | m_ServerURL = serviceURI; | 91 | m_ServerURL = serviceURI; |
92 | if (!m_ServerURL.EndsWith("/")) | ||
93 | m_ServerURL += "/"; | ||
90 | 94 | ||
91 | m_log.DebugFormat("[USER AGENT CONNECTOR]: UserAgentServiceConnector started for {0}", m_ServerURL); | 95 | m_log.DebugFormat("[USER AGENT CONNECTOR]: UserAgentServiceConnector started for {0}", m_ServerURL); |
92 | } | 96 | } |
diff --git a/OpenSim/Services/GridService/HypergridLinker.cs b/OpenSim/Services/GridService/HypergridLinker.cs index c02c813..588c1dd 100644 --- a/OpenSim/Services/GridService/HypergridLinker.cs +++ b/OpenSim/Services/GridService/HypergridLinker.cs | |||
@@ -162,6 +162,7 @@ namespace OpenSim.Services.GridService | |||
162 | 162 | ||
163 | #region Link Region | 163 | #region Link Region |
164 | 164 | ||
165 | // from map search | ||
165 | public GridRegion LinkRegion(UUID scopeID, string regionDescriptor) | 166 | public GridRegion LinkRegion(UUID scopeID, string regionDescriptor) |
166 | { | 167 | { |
167 | string reason = string.Empty; | 168 | string reason = string.Empty; |
@@ -171,7 +172,7 @@ namespace OpenSim.Services.GridService | |||
171 | 172 | ||
172 | private static Random random = new Random(); | 173 | private static Random random = new Random(); |
173 | 174 | ||
174 | // From the command line link-region | 175 | // From the command line link-region (obsolete) and the map |
175 | public GridRegion TryLinkRegionToCoords(UUID scopeID, string mapName, int xloc, int yloc, out string reason) | 176 | public GridRegion TryLinkRegionToCoords(UUID scopeID, string mapName, int xloc, int yloc, out string reason) |
176 | { | 177 | { |
177 | return TryLinkRegionToCoords(scopeID, mapName, xloc, yloc, UUID.Zero, out reason); | 178 | return TryLinkRegionToCoords(scopeID, mapName, xloc, yloc, UUID.Zero, out reason); |
@@ -180,45 +181,48 @@ namespace OpenSim.Services.GridService | |||
180 | public GridRegion TryLinkRegionToCoords(UUID scopeID, string mapName, int xloc, int yloc, UUID ownerID, out string reason) | 181 | public GridRegion TryLinkRegionToCoords(UUID scopeID, string mapName, int xloc, int yloc, UUID ownerID, out string reason) |
181 | { | 182 | { |
182 | reason = string.Empty; | 183 | reason = string.Empty; |
183 | string host = "127.0.0.1"; | 184 | GridRegion regInfo = null; |
184 | string portstr; | 185 | |
185 | string regionName = ""; | 186 | if (!mapName.StartsWith("http")) |
186 | uint port = 0; | ||
187 | string[] parts = mapName.Split(new char[] { ':' }); | ||
188 | if (parts.Length >= 1) | ||
189 | { | ||
190 | host = parts[0]; | ||
191 | } | ||
192 | if (parts.Length >= 2) | ||
193 | { | ||
194 | portstr = parts[1]; | ||
195 | //m_log.Debug("-- port = " + portstr); | ||
196 | if (!UInt32.TryParse(portstr, out port)) | ||
197 | regionName = parts[1]; | ||
198 | } | ||
199 | // always take the last one | ||
200 | if (parts.Length >= 3) | ||
201 | { | 187 | { |
202 | regionName = parts[2]; | 188 | string host = "127.0.0.1"; |
203 | } | 189 | string portstr; |
190 | string regionName = ""; | ||
191 | uint port = 0; | ||
192 | string[] parts = mapName.Split(new char[] { ':' }); | ||
193 | if (parts.Length >= 1) | ||
194 | { | ||
195 | host = parts[0]; | ||
196 | } | ||
197 | if (parts.Length >= 2) | ||
198 | { | ||
199 | portstr = parts[1]; | ||
200 | //m_log.Debug("-- port = " + portstr); | ||
201 | if (!UInt32.TryParse(portstr, out port)) | ||
202 | regionName = parts[1]; | ||
203 | } | ||
204 | // always take the last one | ||
205 | if (parts.Length >= 3) | ||
206 | { | ||
207 | regionName = parts[2]; | ||
208 | } | ||
204 | 209 | ||
205 | //// Sanity check. | ||
206 | //try | ||
207 | //{ | ||
208 | // Util.GetHostFromDNS(host); | ||
209 | //} | ||
210 | //catch | ||
211 | //{ | ||
212 | // reason = "Malformed hostname"; | ||
213 | // return null; | ||
214 | //} | ||
215 | 210 | ||
216 | GridRegion regInfo; | 211 | bool success = TryCreateLink(scopeID, xloc, yloc, regionName, port, host, ownerID, out regInfo, out reason); |
217 | bool success = TryCreateLink(scopeID, xloc, yloc, regionName, port, host, ownerID, out regInfo, out reason); | 212 | if (success) |
218 | if (success) | 213 | { |
214 | regInfo.RegionName = mapName; | ||
215 | return regInfo; | ||
216 | } | ||
217 | } | ||
218 | else | ||
219 | { | 219 | { |
220 | regInfo.RegionName = mapName; | 220 | string[] parts = mapName.Split(new char[] {' '}); |
221 | return regInfo; | 221 | string regionName = String.Empty; |
222 | if (parts.Length > 1) | ||
223 | regionName = parts[1]; | ||
224 | if (TryCreateLink(scopeID, xloc, yloc, regionName, 0, null, parts[0], ownerID, out regInfo, out reason)) | ||
225 | return regInfo; | ||
222 | } | 226 | } |
223 | 227 | ||
224 | return null; | 228 | return null; |
@@ -231,7 +235,7 @@ namespace OpenSim.Services.GridService | |||
231 | 235 | ||
232 | public bool TryCreateLink(UUID scopeID, int xloc, int yloc, string remoteRegionName, uint externalPort, string externalHostName, string serverURI, UUID ownerID, out GridRegion regInfo, out string reason) | 236 | public bool TryCreateLink(UUID scopeID, int xloc, int yloc, string remoteRegionName, uint externalPort, string externalHostName, string serverURI, UUID ownerID, out GridRegion regInfo, out string reason) |
233 | { | 237 | { |
234 | m_log.DebugFormat("[HYPERGRID LINKER]: Link to {0}:{1}, in {2}-{3}", | 238 | m_log.DebugFormat("[HYPERGRID LINKER]: Link to {0} {1}, in {2}-{3}", |
235 | ((serverURI == null) ? (externalHostName + ":" + externalPort) : serverURI), | 239 | ((serverURI == null) ? (externalHostName + ":" + externalPort) : serverURI), |
236 | remoteRegionName, xloc / Constants.RegionSize, yloc / Constants.RegionSize); | 240 | remoteRegionName, xloc / Constants.RegionSize, yloc / Constants.RegionSize); |
237 | 241 | ||
@@ -324,7 +328,7 @@ namespace OpenSim.Services.GridService | |||
324 | regInfo.TerrainImage = m_GatekeeperConnector.GetMapImage(regionID, imageURL, m_MapTileDirectory); | 328 | regInfo.TerrainImage = m_GatekeeperConnector.GetMapImage(regionID, imageURL, m_MapTileDirectory); |
325 | 329 | ||
326 | AddHyperlinkRegion(regInfo, handle); | 330 | AddHyperlinkRegion(regInfo, handle); |
327 | m_log.Info("[HYPERGRID LINKER]: Successfully linked to region_uuid " + regInfo.RegionID); | 331 | m_log.InfoFormat("[HYPERGRID LINKER]: Successfully linked to region {0} with image {1}", regInfo.RegionName, regInfo.TerrainImage); |
328 | return true; | 332 | return true; |
329 | } | 333 | } |
330 | 334 | ||