aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework
diff options
context:
space:
mode:
authorunknown2010-05-20 12:28:13 -0700
committerunknown2010-05-20 12:28:13 -0700
commitbeed74096cbe7df35b2a8036a89af398f908544d (patch)
tree78cec5ca1f73b35f0b5c30a88d8cd97538bb27e8 /OpenSim/Region/Framework
parentRevert one of the previous patches' hunks. The new code looked better, but (diff)
parent* Don't send texture data for prims in ImprovedTerseObjectUpdate packets unle... (diff)
downloadopensim-SC-beed74096cbe7df35b2a8036a89af398f908544d.zip
opensim-SC-beed74096cbe7df35b2a8036a89af398f908544d.tar.gz
opensim-SC-beed74096cbe7df35b2a8036a89af398f908544d.tar.bz2
opensim-SC-beed74096cbe7df35b2a8036a89af398f908544d.tar.xz
Merging slimupdates2
Diffstat (limited to 'OpenSim/Region/Framework')
-rw-r--r--OpenSim/Region/Framework/Interfaces/ILandChannel.cs3
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneGraph.cs114
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneObjectPart.cs36
-rw-r--r--OpenSim/Region/Framework/Scenes/ScenePresence.cs29
4 files changed, 94 insertions, 88 deletions
diff --git a/OpenSim/Region/Framework/Interfaces/ILandChannel.cs b/OpenSim/Region/Framework/Interfaces/ILandChannel.cs
index f71e31d..20b8ab6 100644
--- a/OpenSim/Region/Framework/Interfaces/ILandChannel.cs
+++ b/OpenSim/Region/Framework/Interfaces/ILandChannel.cs
@@ -76,5 +76,8 @@ namespace OpenSim.Region.Framework.Interfaces
76 void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel); 76 void setParcelObjectMaxOverride(overrideParcelMaxPrimCountDelegate overrideDel);
77 void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel); 77 void setSimulatorObjectMaxOverride(overrideSimulatorMaxPrimCountDelegate overrideDel);
78 void SetParcelOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime); 78 void SetParcelOtherCleanTime(IClientAPI remoteClient, int localID, int otherCleanTime);
79
80 void Join(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id);
81 void Subdivide(int start_x, int start_y, int end_x, int end_y, UUID attempting_user_id);
79 } 82 }
80} 83}
diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs
index ce11267..ef13c98 100644
--- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs
@@ -68,8 +68,9 @@ namespace OpenSim.Region.Framework.Scenes
68 68
69 #region Fields 69 #region Fields
70 70
71 protected Dictionary<UUID, ScenePresence> m_scenePresences = new Dictionary<UUID, ScenePresence>(); 71 protected object m_presenceLock = new object();
72 protected ScenePresence[] m_scenePresenceArray = new ScenePresence[0]; 72 protected Dictionary<UUID, ScenePresence> m_scenePresenceMap = new Dictionary<UUID, ScenePresence>();
73 protected List<ScenePresence> m_scenePresenceArray = new List<ScenePresence>();
73 74
74 // SceneObjects is not currently populated or used. 75 // SceneObjects is not currently populated or used.
75 //public Dictionary<UUID, SceneObjectGroup> SceneObjects; 76 //public Dictionary<UUID, SceneObjectGroup> SceneObjects;
@@ -132,10 +133,12 @@ namespace OpenSim.Region.Framework.Scenes
132 133
133 protected internal void Close() 134 protected internal void Close()
134 { 135 {
135 lock (m_scenePresences) 136 lock (m_presenceLock)
136 { 137 {
137 m_scenePresences.Clear(); 138 Dictionary<UUID, ScenePresence> newmap = new Dictionary<UUID, ScenePresence>();
138 m_scenePresenceArray = new ScenePresence[0]; 139 List<ScenePresence> newlist = new List<ScenePresence>();
140 m_scenePresenceMap = newmap;
141 m_scenePresenceArray = newlist;
139 } 142 }
140 143
141 lock (m_dictionary_lock) 144 lock (m_dictionary_lock)
@@ -518,34 +521,29 @@ namespace OpenSim.Region.Framework.Scenes
518 521
519 Entities[presence.UUID] = presence; 522 Entities[presence.UUID] = presence;
520 523
521 lock (m_scenePresences) 524 lock (m_presenceLock)
522 { 525 {
523 if (!m_scenePresences.ContainsKey(presence.UUID)) 526 Dictionary<UUID, ScenePresence> newmap = new Dictionary<UUID, ScenePresence>(m_scenePresenceMap);
527 List<ScenePresence> newlist = new List<ScenePresence>(m_scenePresenceArray);
528
529 if (!newmap.ContainsKey(presence.UUID))
524 { 530 {
525 m_scenePresences.Add(presence.UUID, presence); 531 newmap.Add(presence.UUID, presence);
526 532 newlist.Add(presence);
527 // Create a new array of ScenePresence references
528 int oldLength = m_scenePresenceArray.Length;
529 ScenePresence[] newArray = new ScenePresence[oldLength + 1];
530 Array.Copy(m_scenePresenceArray, newArray, oldLength);
531 newArray[oldLength] = presence;
532 m_scenePresenceArray = newArray;
533 } 533 }
534 else 534 else
535 { 535 {
536 m_scenePresences[presence.UUID] = presence; 536 // Remember the old presene reference from the dictionary
537 537 ScenePresence oldref = newmap[presence.UUID];
538 // Do a linear search through the array of ScenePresence references 538 // Replace the presence reference in the dictionary with the new value
539 // and update the modified entry 539 newmap[presence.UUID] = presence;
540 for (int i = 0; i < m_scenePresenceArray.Length; i++) 540 // Find the index in the list where the old ref was stored and update the reference
541 { 541 newlist[newlist.IndexOf(oldref)] = presence;
542 if (m_scenePresenceArray[i].UUID == presence.UUID)
543 {
544 m_scenePresenceArray[i] = presence;
545 break;
546 }
547 }
548 } 542 }
543
544 // Swap out the dictionary and list with new references
545 m_scenePresenceMap = newmap;
546 m_scenePresenceArray = newlist;
549 } 547 }
550 } 548 }
551 549
@@ -561,25 +559,21 @@ namespace OpenSim.Region.Framework.Scenes
561 agentID); 559 agentID);
562 } 560 }
563 561
564 lock (m_scenePresences) 562 lock (m_presenceLock)
565 { 563 {
566 if (m_scenePresences.Remove(agentID)) 564 Dictionary<UUID, ScenePresence> newmap = new Dictionary<UUID, ScenePresence>(m_scenePresenceMap);
565 List<ScenePresence> newlist = new List<ScenePresence>(m_scenePresenceArray);
566
567 // Remember the old presene reference from the dictionary
568 ScenePresence oldref = newmap[agentID];
569 // Remove the presence reference from the dictionary
570 if (newmap.Remove(agentID))
567 { 571 {
568 // Copy all of the elements from the previous array 572 // Find the index in the list where the old ref was stored and remove the reference
569 // into the new array except the removed element 573 newlist.RemoveAt(newlist.IndexOf(oldref));
570 int oldLength = m_scenePresenceArray.Length; 574 // Swap out the dictionary and list with new references
571 ScenePresence[] newArray = new ScenePresence[oldLength - 1]; 575 m_scenePresenceMap = newmap;
572 int j = 0; 576 m_scenePresenceArray = newlist;
573 for (int i = 0; i < m_scenePresenceArray.Length; i++)
574 {
575 ScenePresence presence = m_scenePresenceArray[i];
576 if (presence.UUID != agentID)
577 {
578 newArray[j] = presence;
579 ++j;
580 }
581 }
582 m_scenePresenceArray = newArray;
583 } 577 }
584 else 578 else
585 { 579 {
@@ -698,7 +692,7 @@ namespace OpenSim.Region.Framework.Scenes
698 } 692 }
699 693
700 /// <summary> 694 /// <summary>
701 /// Request a copy of m_scenePresences in this World 695 /// Get a reference to the scene presence list. Changes to the list will be done in a copy
702 /// There is no guarantee that presences will remain in the scene after the list is returned. 696 /// There is no guarantee that presences will remain in the scene after the list is returned.
703 /// This list should remain private to SceneGraph. Callers wishing to iterate should instead 697 /// This list should remain private to SceneGraph. Callers wishing to iterate should instead
704 /// pass a delegate to ForEachScenePresence. 698 /// pass a delegate to ForEachScenePresence.
@@ -706,8 +700,7 @@ namespace OpenSim.Region.Framework.Scenes
706 /// <returns></returns> 700 /// <returns></returns>
707 private List<ScenePresence> GetScenePresences() 701 private List<ScenePresence> GetScenePresences()
708 { 702 {
709 lock (m_scenePresences) 703 return m_scenePresenceArray;
710 return new List<ScenePresence>(m_scenePresenceArray);
711 } 704 }
712 705
713 /// <summary> 706 /// <summary>
@@ -717,12 +710,10 @@ namespace OpenSim.Region.Framework.Scenes
717 /// <returns>null if the presence was not found</returns> 710 /// <returns>null if the presence was not found</returns>
718 protected internal ScenePresence GetScenePresence(UUID agentID) 711 protected internal ScenePresence GetScenePresence(UUID agentID)
719 { 712 {
720 ScenePresence sp; 713 Dictionary<UUID, ScenePresence> presences = m_scenePresenceMap;
721 lock (m_scenePresences) 714 ScenePresence presence;
722 { 715 presences.TryGetValue(agentID, out presence);
723 m_scenePresences.TryGetValue(agentID, out sp); 716 return presence;
724 }
725 return sp;
726 } 717 }
727 718
728 /// <summary> 719 /// <summary>
@@ -733,7 +724,8 @@ namespace OpenSim.Region.Framework.Scenes
733 /// <returns>null if the presence was not found</returns> 724 /// <returns>null if the presence was not found</returns>
734 protected internal ScenePresence GetScenePresence(string firstName, string lastName) 725 protected internal ScenePresence GetScenePresence(string firstName, string lastName)
735 { 726 {
736 foreach (ScenePresence presence in GetScenePresences()) 727 List<ScenePresence> presences = GetScenePresences();
728 foreach (ScenePresence presence in presences)
737 { 729 {
738 if (presence.Firstname == firstName && presence.Lastname == lastName) 730 if (presence.Firstname == firstName && presence.Lastname == lastName)
739 return presence; 731 return presence;
@@ -748,7 +740,8 @@ namespace OpenSim.Region.Framework.Scenes
748 /// <returns>null if the presence was not found</returns> 740 /// <returns>null if the presence was not found</returns>
749 protected internal ScenePresence GetScenePresence(uint localID) 741 protected internal ScenePresence GetScenePresence(uint localID)
750 { 742 {
751 foreach (ScenePresence presence in GetScenePresences()) 743 List<ScenePresence> presences = GetScenePresences();
744 foreach (ScenePresence presence in presences)
752 if (presence.LocalId == localID) 745 if (presence.LocalId == localID)
753 return presence; 746 return presence;
754 return null; 747 return null;
@@ -756,10 +749,8 @@ namespace OpenSim.Region.Framework.Scenes
756 749
757 protected internal bool TryGetScenePresence(UUID agentID, out ScenePresence avatar) 750 protected internal bool TryGetScenePresence(UUID agentID, out ScenePresence avatar)
758 { 751 {
759 lock (m_scenePresences) 752 Dictionary<UUID, ScenePresence> presences = m_scenePresenceMap;
760 { 753 presences.TryGetValue(agentID, out avatar);
761 m_scenePresences.TryGetValue(agentID, out avatar);
762 }
763 return (avatar != null); 754 return (avatar != null);
764 } 755 }
765 756
@@ -1036,8 +1027,9 @@ namespace OpenSim.Region.Framework.Scenes
1036 }); 1027 });
1037 Parallel.ForEach<ScenePresence>(GetScenePresences(), protectedAction); 1028 Parallel.ForEach<ScenePresence>(GetScenePresences(), protectedAction);
1038 */ 1029 */
1039 // For now, perform actiona serially 1030 // For now, perform actions serially
1040 foreach (ScenePresence sp in GetScenePresences()) 1031 List<ScenePresence> presences = GetScenePresences();
1032 foreach (ScenePresence sp in presences)
1041 { 1033 {
1042 try 1034 try
1043 { 1035 {
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
index 46eadee..71c8018 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
@@ -104,7 +104,7 @@ namespace OpenSim.Region.Framework.Scenes
104 104
105 #endregion Enumerations 105 #endregion Enumerations
106 106
107 public class SceneObjectPart : IScriptHost 107 public class SceneObjectPart : IScriptHost, ISceneEntity
108 { 108 {
109 /// <value> 109 /// <value>
110 /// Denote all sides of the prim 110 /// Denote all sides of the prim
@@ -712,6 +712,24 @@ namespace OpenSim.Region.Framework.Scenes
712 } 712 }
713 } 713 }
714 714
715 public Vector3 RelativePosition
716 {
717 get
718 {
719 if (IsRoot)
720 {
721 if (IsAttachment)
722 return AttachedPos;
723 else
724 return AbsolutePosition;
725 }
726 else
727 {
728 return OffsetPosition;
729 }
730 }
731 }
732
715 public Quaternion RotationOffset 733 public Quaternion RotationOffset
716 { 734 {
717 get 735 get
@@ -973,7 +991,6 @@ namespace OpenSim.Region.Framework.Scenes
973 get { return AggregateScriptEvents; } 991 get { return AggregateScriptEvents; }
974 } 992 }
975 993
976
977 public Quaternion SitTargetOrientation 994 public Quaternion SitTargetOrientation
978 { 995 {
979 get { return m_sitTargetOrientation; } 996 get { return m_sitTargetOrientation; }
@@ -2925,11 +2942,7 @@ namespace OpenSim.Region.Framework.Scenes
2925 //if (LocalId != ParentGroup.RootPart.LocalId) 2942 //if (LocalId != ParentGroup.RootPart.LocalId)
2926 //isattachment = ParentGroup.RootPart.IsAttachment; 2943 //isattachment = ParentGroup.RootPart.IsAttachment;
2927 2944
2928 byte[] color = new byte[] {m_color.R, m_color.G, m_color.B, m_color.A}; 2945 remoteClient.SendPrimUpdate(this, PrimUpdateFlags.FullUpdate);
2929 remoteClient.SendPrimitiveToClient(new SendPrimitiveData(m_regionHandle, m_parentGroup.GetTimeDilation(), LocalId, m_shape,
2930 lPos, Velocity, Acceleration, RotationOffset, AngularVelocity, clientFlags, m_uuid, _ownerID,
2931 m_text, color, _parentID, m_particleSystem, m_clickAction, (byte)m_material, m_TextureAnimation, IsAttachment,
2932 AttachmentPoint,FromItemID, Sound, SoundGain, SoundFlags, SoundRadius, ParentGroup.GetUpdatePriority(remoteClient)));
2933 } 2946 }
2934 2947
2935 /// <summary> 2948 /// <summary>
@@ -4640,11 +4653,7 @@ namespace OpenSim.Region.Framework.Scenes
4640 4653
4641 // Causes this thread to dig into the Client Thread Data. 4654 // Causes this thread to dig into the Client Thread Data.
4642 // Remember your locking here! 4655 // Remember your locking here!
4643 remoteClient.SendPrimTerseUpdate(new SendPrimitiveTerseData(m_regionHandle, 4656 remoteClient.SendPrimUpdate(this, PrimUpdateFlags.Position | PrimUpdateFlags.Rotation | PrimUpdateFlags.Velocity | PrimUpdateFlags.Acceleration | PrimUpdateFlags.AngularVelocity);
4644 m_parentGroup.GetTimeDilation(), LocalId, lPos,
4645 RotationOffset, Velocity, Acceleration,
4646 AngularVelocity, FromItemID,
4647 OwnerID, (int)AttachmentPoint, null, ParentGroup.GetUpdatePriority(remoteClient)));
4648 } 4657 }
4649 4658
4650 public void AddScriptLPS(int count) 4659 public void AddScriptLPS(int count)
@@ -4694,7 +4703,8 @@ namespace OpenSim.Region.Framework.Scenes
4694 4703
4695 public Color4 GetTextColor() 4704 public Color4 GetTextColor()
4696 { 4705 {
4697 return new Color4((byte)Color.R, (byte)Color.G, (byte)Color.B, (byte)(0xFF - Color.A)); 4706 Color color = Color;
4707 return new Color4(color.R, color.G, color.B, (byte)(0xFF - color.A));
4698 } 4708 }
4699 } 4709 }
4700} 4710}
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
index 30eafd7..ee0eb07 100644
--- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs
+++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
@@ -67,7 +67,7 @@ namespace OpenSim.Region.Framework.Scenes
67 67
68 public delegate void SendCourseLocationsMethod(UUID scene, ScenePresence presence); 68 public delegate void SendCourseLocationsMethod(UUID scene, ScenePresence presence);
69 69
70 public class ScenePresence : EntityBase 70 public class ScenePresence : EntityBase, ISceneEntity
71 { 71 {
72// ~ScenePresence() 72// ~ScenePresence()
73// { 73// {
@@ -478,6 +478,12 @@ namespace OpenSim.Region.Framework.Scenes
478 } 478 }
479 } 479 }
480 480
481 public Vector3 OffsetPosition
482 {
483 get { return m_pos; }
484 set { m_pos = value; }
485 }
486
481 /// <summary> 487 /// <summary>
482 /// Current velocity of the avatar. 488 /// Current velocity of the avatar.
483 /// </summary> 489 /// </summary>
@@ -1036,8 +1042,9 @@ namespace OpenSim.Region.Framework.Scenes
1036 AbsolutePosition = AbsolutePosition + new Vector3(0f, 0f, (1.56f / 6f)); 1042 AbsolutePosition = AbsolutePosition + new Vector3(0f, 0f, (1.56f / 6f));
1037 } 1043 }
1038 1044
1039 ControllingClient.SendAvatarTerseUpdate(new SendAvatarTerseData(m_rootRegionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), LocalId, 1045 ControllingClient.SendPrimUpdate(this, PrimUpdateFlags.Position);
1040 AbsolutePosition, Velocity, Vector3.Zero, m_bodyRot, new Vector4(0,0,1,AbsolutePosition.Z - 0.5f), m_uuid, null, GetUpdatePriority(ControllingClient))); 1046 //ControllingClient.SendAvatarTerseUpdate(new SendAvatarTerseData(m_rootRegionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), LocalId,
1047 // AbsolutePosition, Velocity, Vector3.Zero, m_bodyRot, new Vector4(0,0,1,AbsolutePosition.Z - 0.5f), m_uuid, null, GetUpdatePriority(ControllingClient)));
1041 } 1048 }
1042 1049
1043 public void AddNeighbourRegion(ulong regionHandle, string cap) 1050 public void AddNeighbourRegion(ulong regionHandle, string cap)
@@ -2360,8 +2367,7 @@ namespace OpenSim.Region.Framework.Scenes
2360 2367
2361 //m_log.DebugFormat("[SCENEPRESENCE]: TerseUpdate: Pos={0} Rot={1} Vel={2}", m_pos, m_bodyRot, m_velocity); 2368 //m_log.DebugFormat("[SCENEPRESENCE]: TerseUpdate: Pos={0} Rot={1} Vel={2}", m_pos, m_bodyRot, m_velocity);
2362 2369
2363 remoteClient.SendAvatarTerseUpdate(new SendAvatarTerseData(m_rootRegionHandle, (ushort)(m_scene.TimeDilation * ushort.MaxValue), LocalId, 2370 remoteClient.SendPrimUpdate(this, PrimUpdateFlags.Position | PrimUpdateFlags.Rotation | PrimUpdateFlags.Velocity | PrimUpdateFlags.Acceleration | PrimUpdateFlags.AngularVelocity);
2364 pos, velocity, Vector3.Zero, m_bodyRot, CollisionPlane, m_uuid, null, GetUpdatePriority(remoteClient)));
2365 2371
2366 m_scene.StatsReporter.AddAgentTime(Util.EnvironmentTickCountSubtract(m_perfMonMS)); 2372 m_scene.StatsReporter.AddAgentTime(Util.EnvironmentTickCountSubtract(m_perfMonMS));
2367 m_scene.StatsReporter.AddAgentUpdates(1); 2373 m_scene.StatsReporter.AddAgentUpdates(1);
@@ -2457,9 +2463,7 @@ namespace OpenSim.Region.Framework.Scenes
2457 Vector3 pos = m_pos; 2463 Vector3 pos = m_pos;
2458 pos.Z += m_appearance.HipOffset; 2464 pos.Z += m_appearance.HipOffset;
2459 2465
2460 remoteAvatar.m_controllingClient.SendAvatarData(new SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, 2466 remoteAvatar.m_controllingClient.SendAvatarDataImmediate(this);
2461 LocalId, pos, m_appearance.Texture.GetBytes(),
2462 m_parentID, m_bodyRot));
2463 m_scene.StatsReporter.AddAgentUpdates(1); 2467 m_scene.StatsReporter.AddAgentUpdates(1);
2464 } 2468 }
2465 2469
@@ -2527,8 +2531,7 @@ namespace OpenSim.Region.Framework.Scenes
2527 Vector3 pos = m_pos; 2531 Vector3 pos = m_pos;
2528 pos.Z += m_appearance.HipOffset; 2532 pos.Z += m_appearance.HipOffset;
2529 2533
2530 m_controllingClient.SendAvatarData(new SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, LocalId, 2534 m_controllingClient.SendAvatarDataImmediate(this);
2531 pos, m_appearance.Texture.GetBytes(), m_parentID, m_bodyRot));
2532 2535
2533 SendInitialFullUpdateToAllClients(); 2536 SendInitialFullUpdateToAllClients();
2534 SendAppearanceToAllOtherAgents(); 2537 SendAppearanceToAllOtherAgents();
@@ -2638,9 +2641,7 @@ namespace OpenSim.Region.Framework.Scenes
2638 Vector3 pos = m_pos; 2641 Vector3 pos = m_pos;
2639 pos.Z += m_appearance.HipOffset; 2642 pos.Z += m_appearance.HipOffset;
2640 2643
2641 m_controllingClient.SendAvatarData(new SendAvatarData(m_regionInfo.RegionHandle, m_firstname, m_lastname, m_grouptitle, m_uuid, LocalId, 2644 m_controllingClient.SendAvatarDataImmediate(this);
2642 pos, m_appearance.Texture.GetBytes(), m_parentID, m_bodyRot));
2643
2644 } 2645 }
2645 2646
2646 public void SetWearable(int wearableId, AvatarWearable wearable) 2647 public void SetWearable(int wearableId, AvatarWearable wearable)
@@ -3906,7 +3907,7 @@ namespace OpenSim.Region.Framework.Scenes
3906 3907
3907 private void Reprioritize(object sender, ElapsedEventArgs e) 3908 private void Reprioritize(object sender, ElapsedEventArgs e)
3908 { 3909 {
3909 m_controllingClient.ReprioritizeUpdates(StateUpdateTypes.All, UpdatePriority); 3910 m_controllingClient.ReprioritizeUpdates(UpdatePriority);
3910 3911
3911 lock (m_reprioritization_timer) 3912 lock (m_reprioritization_timer)
3912 { 3913 {