diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs | 544 |
1 files changed, 0 insertions, 544 deletions
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs deleted file mode 100644 index 4e13fb3..0000000 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs +++ /dev/null | |||
@@ -1,544 +0,0 @@ | |||
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 OpenMetaverse; | ||
32 | using OpenSim.Framework; | ||
33 | using OpenSim.Region.CoreModules; | ||
34 | using OpenSim.Region; | ||
35 | using OpenSim.Region.Framework.Scenes; | ||
36 | using OpenSim.Region.Framework.Interfaces; | ||
37 | using OpenSim.Region.ScriptEngine.Shared; | ||
38 | using log4net; | ||
39 | |||
40 | namespace OpenSim.Region.ScriptEngine.DotNetEngine | ||
41 | { | ||
42 | /// <summary> | ||
43 | /// Prepares events so they can be directly executed upon a script by EventQueueManager, then queues it. | ||
44 | /// </summary> | ||
45 | [Serializable] | ||
46 | public class EventManager | ||
47 | { | ||
48 | // | ||
49 | // Class is instanced in "ScriptEngine" and Uses "EventQueueManager" | ||
50 | // that is also instanced in "ScriptEngine". | ||
51 | // This class needs a bit of explaining: | ||
52 | // | ||
53 | // This class it the link between an event inside OpenSim and | ||
54 | // the corresponding event in a user script being executed. | ||
55 | // | ||
56 | // For example when an user touches an object then the | ||
57 | // "myScriptEngine.World.EventManager.OnObjectGrab" event is fired | ||
58 | // inside OpenSim. | ||
59 | // We hook up to this event and queue a touch_start in | ||
60 | // EventQueueManager with the proper LSL parameters. | ||
61 | // It will then be delivered to the script by EventQueueManager. | ||
62 | // | ||
63 | // You can check debug C# dump of an LSL script if you need to | ||
64 | // verify what exact parameters are needed. | ||
65 | // | ||
66 | |||
67 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||
68 | |||
69 | private ScriptEngine myScriptEngine; | ||
70 | |||
71 | public EventManager(ScriptEngine _ScriptEngine, bool performHookUp) | ||
72 | { | ||
73 | myScriptEngine = _ScriptEngine; | ||
74 | ReadConfig(); | ||
75 | |||
76 | if (performHookUp) | ||
77 | { | ||
78 | myScriptEngine.World.EventManager.OnRezScript += OnRezScript; | ||
79 | } | ||
80 | } | ||
81 | |||
82 | public void HookUpEvents() | ||
83 | { | ||
84 | m_log.Info("[" + myScriptEngine.ScriptEngineName + | ||
85 | "]: Hooking up to server events"); | ||
86 | |||
87 | myScriptEngine.World.EventManager.OnObjectGrab += | ||
88 | touch_start; | ||
89 | myScriptEngine.World.EventManager.OnObjectDeGrab += | ||
90 | touch_end; | ||
91 | myScriptEngine.World.EventManager.OnRemoveScript += | ||
92 | OnRemoveScript; | ||
93 | myScriptEngine.World.EventManager.OnScriptChangedEvent += | ||
94 | changed; | ||
95 | myScriptEngine.World.EventManager.OnScriptAtTargetEvent += | ||
96 | at_target; | ||
97 | myScriptEngine.World.EventManager.OnScriptNotAtTargetEvent += | ||
98 | not_at_target; | ||
99 | myScriptEngine.World.EventManager.OnScriptControlEvent += | ||
100 | control; | ||
101 | myScriptEngine.World.EventManager.OnScriptColliderStart += | ||
102 | collision_start; | ||
103 | myScriptEngine.World.EventManager.OnScriptColliding += | ||
104 | collision; | ||
105 | myScriptEngine.World.EventManager.OnScriptCollidingEnd += | ||
106 | collision_end; | ||
107 | |||
108 | IMoneyModule money = | ||
109 | myScriptEngine.World.RequestModuleInterface<IMoneyModule>(); | ||
110 | if (money != null) | ||
111 | money.OnObjectPaid+=HandleObjectPaid; | ||
112 | } | ||
113 | |||
114 | public void ReadConfig() | ||
115 | { | ||
116 | } | ||
117 | |||
118 | private void HandleObjectPaid(UUID objectID, UUID agentID, int amount) | ||
119 | { | ||
120 | SceneObjectPart part = | ||
121 | myScriptEngine.World.GetSceneObjectPart(objectID); | ||
122 | |||
123 | if (part != null) | ||
124 | { | ||
125 | money(part.LocalId, agentID, amount); | ||
126 | } | ||
127 | } | ||
128 | |||
129 | public void changed(uint localID, uint change) | ||
130 | { | ||
131 | // Add to queue for all scripts in localID, Object pass change. | ||
132 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
133 | "changed",new object[] { new LSL_Types.LSLInteger(change) }, | ||
134 | new DetectParams[0])); | ||
135 | } | ||
136 | |||
137 | public void state_entry(uint localID) | ||
138 | { | ||
139 | // Add to queue for all scripts in ObjectID object | ||
140 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
141 | "state_entry",new object[] { }, | ||
142 | new DetectParams[0])); | ||
143 | } | ||
144 | |||
145 | public void touch_start(uint localID, uint originalID, | ||
146 | Vector3 offsetPos, IClientAPI remoteClient, SurfaceTouchEventArgs surfaceArgs) | ||
147 | { | ||
148 | // Add to queue for all scripts in ObjectID object | ||
149 | DetectParams[] det = new DetectParams[1]; | ||
150 | det[0] = new DetectParams(); | ||
151 | det[0].Key = remoteClient.AgentId; | ||
152 | det[0].Populate(myScriptEngine.World); | ||
153 | |||
154 | if (originalID == 0) | ||
155 | { | ||
156 | SceneObjectPart part = | ||
157 | myScriptEngine.World.GetSceneObjectPart(localID); | ||
158 | |||
159 | if (part == null) | ||
160 | return; | ||
161 | |||
162 | det[0].LinkNum = part.LinkNum; | ||
163 | } | ||
164 | else | ||
165 | { | ||
166 | SceneObjectPart originalPart = | ||
167 | myScriptEngine.World.GetSceneObjectPart(originalID); | ||
168 | det[0].LinkNum = originalPart.LinkNum; | ||
169 | } | ||
170 | if (surfaceArgs != null) | ||
171 | { | ||
172 | det[0].SurfaceTouchArgs = surfaceArgs; | ||
173 | } | ||
174 | |||
175 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
176 | "touch_start", new Object[] { new LSL_Types.LSLInteger(1) }, | ||
177 | det)); | ||
178 | } | ||
179 | |||
180 | public void touch(uint localID, uint originalID, Vector3 offsetPos, | ||
181 | IClientAPI remoteClient) | ||
182 | { | ||
183 | // Add to queue for all scripts in ObjectID object | ||
184 | DetectParams[] det = new DetectParams[1]; | ||
185 | det[0] = new DetectParams(); | ||
186 | det[0].Key = remoteClient.AgentId; | ||
187 | det[0].Populate(myScriptEngine.World); | ||
188 | det[0].OffsetPos = new LSL_Types.Vector3(offsetPos.X, | ||
189 | offsetPos.Y, | ||
190 | offsetPos.Z); | ||
191 | |||
192 | if (originalID == 0) | ||
193 | { | ||
194 | SceneObjectPart part = myScriptEngine.World.GetSceneObjectPart(localID); | ||
195 | if (part == null) | ||
196 | return; | ||
197 | |||
198 | det[0].LinkNum = part.LinkNum; | ||
199 | } | ||
200 | else | ||
201 | { | ||
202 | SceneObjectPart originalPart = myScriptEngine.World.GetSceneObjectPart(originalID); | ||
203 | det[0].LinkNum = originalPart.LinkNum; | ||
204 | } | ||
205 | |||
206 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
207 | "touch", new Object[] { new LSL_Types.LSLInteger(1) }, | ||
208 | det)); | ||
209 | } | ||
210 | |||
211 | public void touch_end(uint localID, uint originalID, IClientAPI remoteClient, | ||
212 | SurfaceTouchEventArgs surfaceArgs) | ||
213 | { | ||
214 | // Add to queue for all scripts in ObjectID object | ||
215 | DetectParams[] det = new DetectParams[1]; | ||
216 | det[0] = new DetectParams(); | ||
217 | det[0].Key = remoteClient.AgentId; | ||
218 | det[0].Populate(myScriptEngine.World); | ||
219 | |||
220 | if (originalID == 0) | ||
221 | { | ||
222 | SceneObjectPart part = | ||
223 | myScriptEngine.World.GetSceneObjectPart(localID); | ||
224 | if (part == null) | ||
225 | return; | ||
226 | |||
227 | det[0].LinkNum = part.LinkNum; | ||
228 | } | ||
229 | else | ||
230 | { | ||
231 | SceneObjectPart originalPart = | ||
232 | myScriptEngine.World.GetSceneObjectPart(originalID); | ||
233 | det[0].LinkNum = originalPart.LinkNum; | ||
234 | } | ||
235 | |||
236 | if (surfaceArgs != null) | ||
237 | { | ||
238 | det[0].SurfaceTouchArgs = surfaceArgs; | ||
239 | } | ||
240 | |||
241 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
242 | "touch_end", new Object[] { new LSL_Types.LSLInteger(1) }, | ||
243 | det)); | ||
244 | } | ||
245 | |||
246 | public void OnRezScript(uint localID, UUID itemID, string script, | ||
247 | int startParam, bool postOnRez, string engine, int stateSource) | ||
248 | { | ||
249 | if (script.StartsWith("//MRM:")) | ||
250 | return; | ||
251 | |||
252 | List<IScriptModule> engines = | ||
253 | new List<IScriptModule>( | ||
254 | myScriptEngine.World.RequestModuleInterfaces<IScriptModule>()); | ||
255 | |||
256 | List<string> names = new List<string>(); | ||
257 | foreach (IScriptModule m in engines) | ||
258 | names.Add(m.ScriptEngineName); | ||
259 | |||
260 | int lineEnd = script.IndexOf('\n'); | ||
261 | |||
262 | if (lineEnd > 1) | ||
263 | { | ||
264 | string firstline = script.Substring(0, lineEnd).Trim(); | ||
265 | |||
266 | int colon = firstline.IndexOf(':'); | ||
267 | if (firstline.Length > 2 && | ||
268 | firstline.Substring(0, 2) == "//" && colon != -1) | ||
269 | { | ||
270 | string engineName = firstline.Substring(2, colon-2); | ||
271 | |||
272 | if (names.Contains(engineName)) | ||
273 | { | ||
274 | engine = engineName; | ||
275 | script = "//" + script.Substring(script.IndexOf(':')+1); | ||
276 | } | ||
277 | else | ||
278 | { | ||
279 | if (engine == myScriptEngine.ScriptEngineName) | ||
280 | { | ||
281 | SceneObjectPart part = | ||
282 | myScriptEngine.World.GetSceneObjectPart( | ||
283 | localID); | ||
284 | |||
285 | TaskInventoryItem item = | ||
286 | part.Inventory.GetInventoryItem(itemID); | ||
287 | |||
288 | ScenePresence presence = | ||
289 | myScriptEngine.World.GetScenePresence( | ||
290 | item.OwnerID); | ||
291 | |||
292 | if (presence != null) | ||
293 | { | ||
294 | presence.ControllingClient.SendAgentAlertMessage( | ||
295 | "Selected engine unavailable. "+ | ||
296 | "Running script on "+ | ||
297 | myScriptEngine.ScriptEngineName, | ||
298 | false); | ||
299 | } | ||
300 | } | ||
301 | } | ||
302 | } | ||
303 | } | ||
304 | |||
305 | if (engine != myScriptEngine.ScriptEngineName) | ||
306 | return; | ||
307 | |||
308 | // m_log.Debug("OnRezScript localID: " + localID + | ||
309 | // " LLUID: " + itemID.ToString() + " Size: " + | ||
310 | // script.Length); | ||
311 | |||
312 | myScriptEngine.m_ScriptManager.StartScript(localID, itemID, script, | ||
313 | startParam, postOnRez); | ||
314 | } | ||
315 | |||
316 | public void OnRemoveScript(uint localID, UUID itemID) | ||
317 | { | ||
318 | // m_log.Debug("OnRemoveScript localID: " + localID + " LLUID: " + itemID.ToString()); | ||
319 | myScriptEngine.m_ScriptManager.StopScript( | ||
320 | localID, | ||
321 | itemID | ||
322 | ); | ||
323 | } | ||
324 | |||
325 | public void money(uint localID, UUID agentID, int amount) | ||
326 | { | ||
327 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
328 | "money", new object[] { | ||
329 | new LSL_Types.LSLString(agentID.ToString()), | ||
330 | new LSL_Types.LSLInteger(amount) }, | ||
331 | new DetectParams[0])); | ||
332 | } | ||
333 | |||
334 | // TODO: Replace placeholders below | ||
335 | // NOTE! THE PARAMETERS FOR THESE FUNCTIONS ARE NOT CORRECT! | ||
336 | // These needs to be hooked up to OpenSim during init of this class | ||
337 | // then queued in EventQueueManager. | ||
338 | // When queued in EventQueueManager they need to be LSL compatible (name and params) | ||
339 | |||
340 | public void state_exit(uint localID) | ||
341 | { | ||
342 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
343 | "state_exit", new object[] { }, | ||
344 | new DetectParams[0])); | ||
345 | } | ||
346 | |||
347 | public void collision_start(uint localID, ColliderArgs col) | ||
348 | { | ||
349 | // Add to queue for all scripts in ObjectID object | ||
350 | List<DetectParams> det = new List<DetectParams>(); | ||
351 | |||
352 | foreach (DetectedObject detobj in col.Colliders) | ||
353 | { | ||
354 | DetectParams d = new DetectParams(); | ||
355 | d.Key =detobj.keyUUID; | ||
356 | d.Populate(myScriptEngine.World); | ||
357 | det.Add(d); | ||
358 | } | ||
359 | |||
360 | if (det.Count > 0) | ||
361 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
362 | "collision_start", | ||
363 | new Object[] { new LSL_Types.LSLInteger(det.Count) }, | ||
364 | det.ToArray())); | ||
365 | } | ||
366 | |||
367 | public void collision(uint localID, ColliderArgs col) | ||
368 | { | ||
369 | // Add to queue for all scripts in ObjectID object | ||
370 | List<DetectParams> det = new List<DetectParams>(); | ||
371 | |||
372 | foreach (DetectedObject detobj in col.Colliders) | ||
373 | { | ||
374 | DetectParams d = new DetectParams(); | ||
375 | d.Key =detobj.keyUUID; | ||
376 | d.Populate(myScriptEngine.World); | ||
377 | det.Add(d); | ||
378 | } | ||
379 | |||
380 | if (det.Count > 0) | ||
381 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
382 | "collision", new Object[] { new LSL_Types.LSLInteger(det.Count) }, | ||
383 | det.ToArray())); | ||
384 | } | ||
385 | |||
386 | public void collision_end(uint localID, ColliderArgs col) | ||
387 | { | ||
388 | // Add to queue for all scripts in ObjectID object | ||
389 | List<DetectParams> det = new List<DetectParams>(); | ||
390 | |||
391 | foreach (DetectedObject detobj in col.Colliders) | ||
392 | { | ||
393 | DetectParams d = new DetectParams(); | ||
394 | d.Key =detobj.keyUUID; | ||
395 | d.Populate(myScriptEngine.World); | ||
396 | det.Add(d); | ||
397 | } | ||
398 | |||
399 | if (det.Count > 0) | ||
400 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
401 | "collision_end", | ||
402 | new Object[] { new LSL_Types.LSLInteger(det.Count) }, | ||
403 | det.ToArray())); | ||
404 | } | ||
405 | |||
406 | public void land_collision_start(uint localID, UUID itemID) | ||
407 | { | ||
408 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
409 | "land_collision_start", | ||
410 | new object[0], | ||
411 | new DetectParams[0])); | ||
412 | } | ||
413 | |||
414 | public void land_collision(uint localID, UUID itemID) | ||
415 | { | ||
416 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
417 | "land_collision", | ||
418 | new object[0], | ||
419 | new DetectParams[0])); | ||
420 | } | ||
421 | |||
422 | public void land_collision_end(uint localID, UUID itemID) | ||
423 | { | ||
424 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
425 | "land_collision_end", | ||
426 | new object[0], | ||
427 | new DetectParams[0])); | ||
428 | } | ||
429 | |||
430 | // Handled by long commands | ||
431 | public void timer(uint localID, UUID itemID) | ||
432 | { | ||
433 | } | ||
434 | |||
435 | public void listen(uint localID, UUID itemID) | ||
436 | { | ||
437 | } | ||
438 | |||
439 | public void control(uint localID, UUID itemID, UUID agentID, uint held, uint change) | ||
440 | { | ||
441 | if ((change == 0) && (myScriptEngine.m_EventQueueManager.CheckEeventQueueForEvent(localID,"control"))) return; | ||
442 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
443 | "control",new object[] { | ||
444 | new LSL_Types.LSLString(agentID.ToString()), | ||
445 | new LSL_Types.LSLInteger(held), | ||
446 | new LSL_Types.LSLInteger(change)}, | ||
447 | new DetectParams[0])); | ||
448 | } | ||
449 | |||
450 | public void email(uint localID, UUID itemID, string timeSent, | ||
451 | string address, string subject, string message, int numLeft) | ||
452 | { | ||
453 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
454 | "email",new object[] { | ||
455 | new LSL_Types.LSLString(timeSent), | ||
456 | new LSL_Types.LSLString(address), | ||
457 | new LSL_Types.LSLString(subject), | ||
458 | new LSL_Types.LSLString(message), | ||
459 | new LSL_Types.LSLInteger(numLeft)}, | ||
460 | new DetectParams[0])); | ||
461 | } | ||
462 | |||
463 | public void at_target(uint localID, uint handle, Vector3 targetpos, | ||
464 | Vector3 atpos) | ||
465 | { | ||
466 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
467 | "at_target", new object[] { | ||
468 | new LSL_Types.LSLInteger(handle), | ||
469 | new LSL_Types.Vector3(targetpos.X,targetpos.Y,targetpos.Z), | ||
470 | new LSL_Types.Vector3(atpos.X,atpos.Y,atpos.Z) }, | ||
471 | new DetectParams[0])); | ||
472 | } | ||
473 | |||
474 | public void not_at_target(uint localID) | ||
475 | { | ||
476 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
477 | "not_at_target",new object[0], | ||
478 | new DetectParams[0])); | ||
479 | } | ||
480 | |||
481 | public void at_rot_target(uint localID, UUID itemID) | ||
482 | { | ||
483 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
484 | "at_rot_target",new object[0], | ||
485 | new DetectParams[0])); | ||
486 | } | ||
487 | |||
488 | public void not_at_rot_target(uint localID, UUID itemID) | ||
489 | { | ||
490 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
491 | "not_at_rot_target",new object[0], | ||
492 | new DetectParams[0])); | ||
493 | } | ||
494 | |||
495 | public void attach(uint localID, UUID itemID) | ||
496 | { | ||
497 | } | ||
498 | |||
499 | public void dataserver(uint localID, UUID itemID) | ||
500 | { | ||
501 | } | ||
502 | |||
503 | public void link_message(uint localID, UUID itemID) | ||
504 | { | ||
505 | } | ||
506 | |||
507 | public void moving_start(uint localID, UUID itemID) | ||
508 | { | ||
509 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
510 | "moving_start",new object[0], | ||
511 | new DetectParams[0])); | ||
512 | } | ||
513 | |||
514 | public void moving_end(uint localID, UUID itemID) | ||
515 | { | ||
516 | myScriptEngine.PostObjectEvent(localID, new EventParams( | ||
517 | "moving_end",new object[0], | ||
518 | new DetectParams[0])); | ||
519 | } | ||
520 | |||
521 | public void object_rez(uint localID, UUID itemID) | ||
522 | { | ||
523 | } | ||
524 | |||
525 | public void remote_data(uint localID, UUID itemID) | ||
526 | { | ||
527 | } | ||
528 | |||
529 | // Handled by long commands | ||
530 | public void http_response(uint localID, UUID itemID) | ||
531 | { | ||
532 | } | ||
533 | |||
534 | /// <summary> | ||
535 | /// If set to true then threads and stuff should try to make a graceful exit | ||
536 | /// </summary> | ||
537 | public bool PleaseShutdown | ||
538 | { | ||
539 | get { return _PleaseShutdown; } | ||
540 | set { _PleaseShutdown = value; } | ||
541 | } | ||
542 | private bool _PleaseShutdown = false; | ||
543 | } | ||
544 | } | ||