aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs407
-rwxr-xr-xOpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs10
2 files changed, 363 insertions, 54 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index 50bb9ba..662e5ad 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -4770,6 +4770,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4770 30 // ID (high frequency) 4770 30 // ID (high frequency)
4771 }; 4771 };
4772 4772
4773 static private readonly byte[] CompressedObjectHeader = new byte[] {
4774 Helpers.MSG_RELIABLE,
4775 0, 0, 0, 0, // sequence number
4776 0, // extra
4777 13 // ID (high frequency)
4778 };
4779
4773 private void ProcessEntityUpdates(int maxUpdatesBytes) 4780 private void ProcessEntityUpdates(int maxUpdatesBytes)
4774 { 4781 {
4775 if (!IsActive) 4782 if (!IsActive)
@@ -4779,9 +4786,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4779 if (mysp == null) 4786 if (mysp == null)
4780 return; 4787 return;
4781 4788
4782 // List<ObjectUpdateCompressedPacket.ObjectDataBlock> compressedUpdateBlocks = null;
4783 List<EntityUpdate> objectUpdates = null; 4789 List<EntityUpdate> objectUpdates = null;
4784 // List<EntityUpdate> compressedUpdates = null; 4790 //List<EntityUpdate> compressedUpdates = null;
4785 List<EntityUpdate> terseUpdates = null; 4791 List<EntityUpdate> terseUpdates = null;
4786 List<SceneObjectPart> ObjectAnimationUpdates = null; 4792 List<SceneObjectPart> ObjectAnimationUpdates = null;
4787 4793
@@ -4970,17 +4976,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4970 if(updateFlags == PrimUpdateFlags.None) 4976 if(updateFlags == PrimUpdateFlags.None)
4971 continue; 4977 continue;
4972 4978
4973 /*
4974 const PrimUpdateFlags canNotUseCompressedMask =
4975 PrimUpdateFlags.Velocity | PrimUpdateFlags.Acceleration |
4976 PrimUpdateFlags.CollisionPlane | PrimUpdateFlags.Joint;
4977
4978 if ((updateFlags & canNotUseCompressedMask) != 0)
4979 {
4980 canUseCompressed = false;
4981 }
4982 */
4983
4984 const PrimUpdateFlags canNotUseImprovedMask = ~( 4979 const PrimUpdateFlags canNotUseImprovedMask = ~(
4985 PrimUpdateFlags.AttachmentPoint | 4980 PrimUpdateFlags.AttachmentPoint |
4986 PrimUpdateFlags.Position | 4981 PrimUpdateFlags.Position |
@@ -4996,19 +4991,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4996 4991
4997 #region Block Construction 4992 #region Block Construction
4998 4993
4999 // TODO: Remove this once we can build compressed updates
5000 /*
5001 if (canUseCompressed)
5002 {
5003 ObjectUpdateCompressedPacket.ObjectDataBlock ablock =
5004 CreateCompressedUpdateBlock((SceneObjectPart)update.Entity, updateFlags);
5005 compressedUpdateBlocks.Add(ablock);
5006 compressedUpdates.Value.Add(update);
5007 maxUpdatesBytes -= ablock.Length;
5008 }
5009 else if (canUseImproved)
5010 */
5011
5012 if ((updateFlags & canNotUseImprovedMask) == 0) 4994 if ((updateFlags & canNotUseImprovedMask) == 0)
5013 { 4995 {
5014 if (terseUpdates == null) 4996 if (terseUpdates == null)
@@ -5031,16 +5013,47 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5031 else 5013 else
5032 { 5014 {
5033 if (update.Entity is ScenePresence) 5015 if (update.Entity is ScenePresence)
5016 {
5034 maxUpdatesBytes -= 150; // crude estimation 5017 maxUpdatesBytes -= 150; // crude estimation
5035 else
5036 maxUpdatesBytes -= 300;
5037 5018
5038 if(objectUpdates == null) 5019 if (objectUpdates == null)
5020 {
5021 objectUpdates = new List<EntityUpdate>();
5022 maxUpdatesBytes -= 18;
5023 }
5024 objectUpdates.Add(update);
5025 }
5026 else
5039 { 5027 {
5040 objectUpdates = new List<EntityUpdate>(); 5028 SceneObjectPart part = (SceneObjectPart)update.Entity;
5041 maxUpdatesBytes -= 18; 5029 SceneObjectGroup grp = part.ParentGroup;
5030 // minimal compress conditions, not enough ?
5031 //if (grp.UsesPhysics || part.Velocity.LengthSquared() > 1e-8f || part.Acceleration.LengthSquared() > 1e-6f)
5032 {
5033 maxUpdatesBytes -= 150; // crude estimation
5034
5035 if (objectUpdates == null)
5036 {
5037 objectUpdates = new List<EntityUpdate>();
5038 maxUpdatesBytes -= 18;
5039 }
5040 objectUpdates.Add(update);
5041 }
5042 //compress still disabled
5043 /*
5044 else
5045 {
5046 maxUpdatesBytes -= 150; // crude estimation
5047
5048 if (compressedUpdates == null)
5049 {
5050 compressedUpdates = new List<EntityUpdate>();
5051 maxUpdatesBytes -= 18;
5052 }
5053 compressedUpdates.Add(update);
5054 }
5055 */
5042 } 5056 }
5043 objectUpdates.Add(update);
5044 } 5057 }
5045 5058
5046 #endregion Block Construction 5059 #endregion Block Construction
@@ -5067,7 +5080,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5067 zc.Position = 7; 5080 zc.Position = 7;
5068 5081
5069 zc.AddUInt64(m_scene.RegionInfo.RegionHandle); 5082 zc.AddUInt64(m_scene.RegionInfo.RegionHandle);
5070 zc.AddUInt16(Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f)); 5083 zc.AddUInt16(timeDilation);
5071 5084
5072 zc.AddByte(1); // tmp block count 5085 zc.AddByte(1); // tmp block count
5073 5086
@@ -5134,19 +5147,67 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5134 delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false); 5147 delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false);
5135 } 5148 }
5136 } 5149 }
5137 5150 /*
5138/* 5151 if(compressedUpdates != null)
5139 if (compressedUpdateBlocks != null)
5140 { 5152 {
5141 ObjectUpdateCompressedPacket packet = (ObjectUpdateCompressedPacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdateCompressed); 5153 List<EntityUpdate> tau = new List<EntityUpdate>(30);
5142 packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; 5154
5143 packet.RegionData.TimeDilation = timeDilation; 5155 UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint);
5144 packet.ObjectData = compressedUpdateBlocks.ToArray(); 5156 byte[] data = buf.Data;
5145 compressedUpdateBlocks.Clear(); 5157
5158 Buffer.BlockCopy(CompressedObjectHeader, 0, data , 0, 7);
5146 5159
5147 OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(compressedUpdates, oPacket); }); 5160 Utils.UInt64ToBytesSafepos(m_scene.RegionInfo.RegionHandle, data, 7); // 15
5161 Utils.UInt16ToBytes(timeDilation, data, 15); // 17
5162
5163 int countposition = 17; // blocks count position
5164 int pos = 18;
5165
5166 int lastpos = 0;
5167
5168 int count = 0;
5169 foreach (EntityUpdate eu in compressedUpdates)
5170 {
5171 lastpos = pos;
5172 CreateCompressedUpdateBlock((SceneObjectPart)eu.Entity, mysp, data, ref pos);
5173 if (pos < LLUDPServer.MAXPAYLOAD)
5174 {
5175 tau.Add(eu);
5176 ++count;
5177 }
5178 else
5179 {
5180 // we need more packets
5181 UDPPacketBuffer newbuf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint);
5182 Buffer.BlockCopy(buf.Data, 0, newbuf.Data, 0, countposition); // start is the same
5183
5184 buf.Data[countposition] = (byte)count;
5185
5186 buf.DataLength = lastpos;
5187 m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task,
5188 delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false);
5189
5190 buf = newbuf;
5191 data = buf.Data;
5192
5193 pos = 18;
5194 // im lazy now, just do last again
5195 CreateCompressedUpdateBlock((SceneObjectPart)eu.Entity, mysp, data, ref pos);
5196 tau = new List<EntityUpdate>(30);
5197 tau.Add(eu);
5198 count = 1;
5199 }
5200 }
5201
5202 if (count > 0)
5203 {
5204 buf.Data[countposition] = (byte)count;
5205 buf.DataLength = pos;
5206 m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task,
5207 delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false);
5208 }
5148 } 5209 }
5149*/ 5210 */
5150 if (terseUpdates != null) 5211 if (terseUpdates != null)
5151 { 5212 {
5152 int blocks = terseUpdates.Count; 5213 int blocks = terseUpdates.Count;
@@ -5305,7 +5366,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
5305// 5366//
5306 } 5367 }
5307*/ 5368*/
5308 public void ReprioritizeUpdates() 5369 public void ReprioritizeUpdates()
5309 { 5370 {
5310 lock (m_entityUpdates.SyncRoot) 5371 lock (m_entityUpdates.SyncRoot)
5311 m_entityUpdates.Reprioritize(UpdatePriorityHandler); 5372 m_entityUpdates.Reprioritize(UpdatePriorityHandler);
@@ -6986,10 +7047,262 @@ namespace OpenSim.Region.ClientStack.LindenUDP
6986 zc.AddZeros(lastzeros); 7047 zc.AddZeros(lastzeros);
6987 } 7048 }
6988 7049
6989 protected ObjectUpdateCompressedPacket.ObjectDataBlock CreateCompressedUpdateBlock(SceneObjectPart part, PrimUpdateFlags updateFlags) 7050 [Flags]
7051 private enum CompressedFlags : uint
7052 {
7053 None = 0x00,
7054 /// <summary>Unknown</summary>
7055 ScratchPad = 0x01,
7056 /// <summary>Whether the object has a TreeSpecies</summary>
7057 Tree = 0x02,
7058 /// <summary>Whether the object has floating text ala llSetText</summary>
7059 HasText = 0x04,
7060 /// <summary>Whether the object has an active particle system</summary>
7061 HasParticles = 0x08,
7062 /// <summary>Whether the object has sound attached to it</summary>
7063 HasSound = 0x10,
7064 /// <summary>Whether the object is attached to a root object or not</summary>
7065 HasParent = 0x20,
7066 /// <summary>Whether the object has texture animation settings</summary>
7067 TextureAnimation = 0x40,
7068 /// <summary>Whether the object has an angular velocity</summary>
7069 HasAngularVelocity = 0x80,
7070 /// <summary>Whether the object has a name value pairs string</summary>
7071 HasNameValues = 0x100,
7072 /// <summary>Whether the object has a Media URL set</summary>
7073 MediaURL = 0x200
7074 }
7075
7076 ///**** temp hack
7077 private static Random rnd = new Random();
7078
7079 protected void CreateCompressedUpdateBlock(SceneObjectPart part, ScenePresence sp, byte[] dest, ref int pos)
6990 { 7080 {
6991 // TODO: Implement this 7081 // prepare data
6992 return null; 7082 CompressedFlags cflags = CompressedFlags.None;
7083
7084 // prim/update flags
7085
7086 PrimFlags primflags = (PrimFlags)m_scene.Permissions.GenerateClientFlags(part, sp);
7087 // Don't send the CreateSelected flag to everyone
7088 primflags &= ~PrimFlags.CreateSelected;
7089 if (sp.UUID == part.OwnerID)
7090 {
7091 if (part.CreateSelected)
7092 {
7093 // Only send this flag once, then unset it
7094 primflags |= PrimFlags.CreateSelected;
7095 part.CreateSelected = false;
7096 }
7097 }
7098
7099 // first is primFlags
7100 Utils.UIntToBytesSafepos((uint)primflags, dest, pos); pos += 4;
7101
7102 // datablock len to fill later
7103 int lenpos = pos;
7104 pos += 2;
7105
7106 byte state = part.Shape.State;
7107 PCode pcode = (PCode)part.Shape.PCode;
7108
7109 bool hastree = false;
7110 if (pcode == PCode.Grass || pcode == PCode.Tree || pcode == PCode.NewTree)
7111 {
7112 cflags |= CompressedFlags.Tree;
7113 hastree = true;
7114 }
7115
7116 //NameValue and state
7117 byte[] nv = null;
7118 if (part.ParentGroup.IsAttachment)
7119 {
7120 if (part.IsRoot)
7121 nv = Util.StringToBytes256("AttachItemID STRING RW SV " + part.ParentGroup.FromItemID);
7122
7123 int st = (int)part.ParentGroup.AttachmentPoint;
7124 state = (byte)(((st & 0xf0) >> 4) + ((st & 0x0f) << 4)); ;
7125 }
7126
7127 bool hastext = part.Text != null && part.Text.Length > 0;
7128 bool hassound = part.Sound != UUID.Zero || part.SoundFlags != 0;
7129 bool hasps = part.ParticleSystem != null && part.ParticleSystem.Length > 1;
7130 bool hastexanim = part.TextureAnimation != null && part.TextureAnimation.Length > 0;
7131 bool hasangvel = part.AngularVelocity.LengthSquared() > 1e-8f;
7132 bool hasmediaurl = part.MediaUrl != null && part.MediaUrl.Length > 1;
7133
7134 if (hastext)
7135 cflags |= CompressedFlags.HasText;
7136 if (hasps)
7137 cflags |= CompressedFlags.HasParticles;
7138 if (hassound)
7139 cflags |= CompressedFlags.HasSound;
7140 if (part.ParentID != 0)
7141 cflags |= CompressedFlags.HasParent;
7142 if (hastexanim)
7143 cflags |= CompressedFlags.TextureAnimation;
7144 if (hasangvel)
7145 cflags |= CompressedFlags.HasAngularVelocity;
7146 if (hasmediaurl)
7147 cflags |= CompressedFlags.MediaURL;
7148 if (nv != null)
7149 cflags |= CompressedFlags.HasNameValues;
7150
7151 // filter out mesh faces hack
7152 ushort profileBegin = part.Shape.ProfileBegin;
7153 ushort profileHollow = part.Shape.ProfileHollow;
7154 byte profileCurve = part.Shape.ProfileCurve;
7155 byte pathScaleY = part.Shape.PathScaleY;
7156
7157 if (part.Shape.SculptType == (byte)SculptType.Mesh) // filter out hack
7158 {
7159 profileCurve = (byte)(part.Shape.ProfileCurve & 0x0f);
7160 // fix old values that confused viewers
7161 if (profileBegin == 1)
7162 profileBegin = 9375;
7163 if (profileHollow == 1)
7164 profileHollow = 27500;
7165 // fix torus hole size Y that also confuse some viewers
7166 if (profileCurve == (byte)ProfileShape.Circle && pathScaleY < 150)
7167 pathScaleY = 150;
7168 }
7169
7170 part.UUID.ToBytes(dest, pos); pos += 16;
7171 Utils.UIntToBytesSafepos(part.LocalId, dest, pos); pos += 4;
7172 dest[pos++] = (byte)pcode;
7173 dest[pos++] = state;
7174
7175 ///**** temp hack
7176 Utils.UIntToBytesSafepos((uint)rnd.Next(), dest, pos); pos += 4; //CRC needs fix or things will get crazy for now avoid caching
7177 dest[pos++] = part.Material;
7178 dest[pos++] = part.ClickAction;
7179 part.Shape.Scale.ToBytes(dest, pos); pos += 12;
7180 part.RelativePosition.ToBytes(dest, pos); pos += 12;
7181 if(pcode == PCode.Grass)
7182 Vector3.Zero.ToBytes(dest, pos);
7183 else
7184 {
7185 Quaternion rotation = part.RotationOffset;
7186 rotation.Normalize();
7187 rotation.ToBytes(dest, pos);
7188 }
7189 pos += 12;
7190
7191 Utils.UIntToBytesSafepos((uint)cflags, dest, pos); pos += 4;
7192
7193 if (hasps || hassound)
7194 part.OwnerID.ToBytes(dest, pos);
7195 else
7196 UUID.Zero.ToBytes(dest, pos);
7197 pos += 16;
7198
7199 if (hasangvel)
7200 {
7201 part.AngularVelocity.ToBytes(dest, pos); pos += 12;
7202 }
7203 if (part.ParentID != 0)
7204 {
7205 Utils.UIntToBytesSafepos(part.ParentID, dest, pos); pos += 4;
7206 }
7207 if (hastree)
7208 dest[pos++] = state;
7209 if (hastext)
7210 {
7211 byte[] text = Util.StringToBytes256(part.Text); // must be null term
7212 Buffer.BlockCopy(text, 0, dest, pos, text.Length); pos += text.Length;
7213 byte[] tc = part.GetTextColor().GetBytes(false);
7214 Buffer.BlockCopy(tc, 0, dest, pos, tc.Length); pos += tc.Length;
7215 }
7216 if (hasmediaurl)
7217 {
7218 byte[] mu = Util.StringToBytes256(part.MediaUrl); // must be null term
7219 Buffer.BlockCopy(mu, 0, dest, pos, mu.Length); pos += mu.Length;
7220 }
7221 if (hasps)
7222 {
7223 byte[] ps = part.ParticleSystem;
7224 Buffer.BlockCopy(ps, 0, dest, pos, ps.Length); pos += ps.Length;
7225 }
7226 byte[] ex = part.Shape.ExtraParams;
7227 if (ex == null || ex.Length < 2)
7228 dest[pos++] = 0;
7229 else
7230 {
7231 Buffer.BlockCopy(ex, 0, dest, pos, ex.Length); pos += ex.Length;
7232 }
7233 if (hassound)
7234 {
7235 part.Sound.ToBytes(dest, pos); pos += 16;
7236 Utils.FloatToBytesSafepos((float)part.SoundGain, dest, pos); pos += 4;
7237 dest[pos++] = part.SoundFlags;
7238 Utils.FloatToBytesSafepos((float)part.SoundRadius, dest, pos); pos += 4;
7239 }
7240 if (nv != null)
7241 {
7242 Buffer.BlockCopy(nv, 0, dest, pos, nv.Length); pos += nv.Length;
7243 }
7244
7245 dest[pos++] = part.Shape.PathCurve;
7246 Utils.UInt16ToBytes(part.Shape.PathBegin, dest, pos); pos += 2;
7247 Utils.UInt16ToBytes(part.Shape.PathEnd, dest, pos); pos += 2;
7248 dest[pos++] = part.Shape.PathScaleX;
7249 dest[pos++] = pathScaleY;
7250 dest[pos++] = part.Shape.PathShearX;
7251 dest[pos++] = part.Shape.PathShearY;
7252 dest[pos++] = (byte)part.Shape.PathTwist;
7253 dest[pos++] = (byte)part.Shape.PathTwistBegin;
7254 dest[pos++] = (byte)part.Shape.PathRadiusOffset;
7255 dest[pos++] = (byte)part.Shape.PathTaperX;
7256 dest[pos++] = (byte)part.Shape.PathTaperY;
7257 dest[pos++] = part.Shape.PathRevolutions;
7258 dest[pos++] = (byte)part.Shape.PathSkew;
7259 dest[pos++] = profileCurve;
7260 Utils.UInt16ToBytes(profileBegin, dest, pos); pos += 2;
7261 Utils.UInt16ToBytes(part.Shape.ProfileEnd, dest, pos); pos += 2;
7262 Utils.UInt16ToBytes(profileHollow, dest, pos); pos += 2;
7263
7264 byte[] te = part.Shape.TextureEntry;
7265 if (te == null)
7266 {
7267 dest[pos++] = 0;
7268 dest[pos++] = 0;
7269 dest[pos++] = 0;
7270 dest[pos++] = 0;
7271 }
7272 else
7273 {
7274 int len = te.Length & 0x7fff;
7275 dest[pos++] = (byte)len;
7276 dest[pos++] = (byte)(len >> 8);
7277 dest[pos++] = 0;
7278 dest[pos++] = 0;
7279 Buffer.BlockCopy(te, 0, dest, pos, len);
7280 pos += len;
7281 }
7282 if (hastexanim)
7283 {
7284 byte[] ta = part.TextureAnimation;
7285 if (ta == null)
7286 {
7287 dest[pos++] = 0;
7288 dest[pos++] = 0;
7289 dest[pos++] = 0;
7290 dest[pos++] = 0;
7291 }
7292 else
7293 {
7294 int len = ta.Length & 0x7fff;
7295 dest[pos++] = (byte)len;
7296 dest[pos++] = (byte)(len >> 8);
7297 dest[pos++] = 0;
7298 dest[pos++] = 0;
7299 Buffer.BlockCopy(ta, 0, dest, pos, len);
7300 pos += len;
7301 }
7302 }
7303 int totlen = pos - lenpos - 2;
7304 dest[lenpos++] = (byte)totlen;
7305 dest[lenpos++] = (byte)(totlen >> 8);
6993 } 7306 }
6994 7307
6995 public void SendNameReply(UUID profileId, string firstname, string lastname) 7308 public void SendNameReply(UUID profileId, string firstname, string lastname)
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
index c899428..a0b3d21 100755
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs
@@ -466,8 +466,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
466 Throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps * 10e-3f); 466 Throttle = new TokenBucket(null, sceneThrottleBps, sceneThrottleBps * 10e-3f);
467 ThrottleRates = new ThrottleRates(configSource); 467 ThrottleRates = new ThrottleRates(configSource);
468 468
469 Random rnd = new Random(Util.EnvironmentTickCount());
470
471// if (usePools) 469// if (usePools)
472// EnablePools(); 470// EnablePools();
473 } 471 }
@@ -1359,11 +1357,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1359 if (packet.Type == PacketType.UseCircuitCode) 1357 if (packet.Type == PacketType.UseCircuitCode)
1360 { 1358 {
1361 // And if there is a UseCircuitCode pending, also drop it 1359 // And if there is a UseCircuitCode pending, also drop it
1360
1362 lock (m_pendingCache) 1361 lock (m_pendingCache)
1363 { 1362 {
1364 if (m_pendingCache.Contains(endPoint)) 1363 if (m_pendingCache.Contains(endPoint))
1365 { 1364 {
1366 FreeUDPBuffer(buffer); 1365 FreeUDPBuffer(buffer);
1366 SendAckImmediate(endPoint, packet.Header.Sequence); // i hear you shutup
1367 return; 1367 return;
1368 } 1368 }
1369 1369
@@ -1372,6 +1372,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1372 1372
1373 Util.FireAndForget(HandleUseCircuitCode, new object[] { endPoint, packet }); 1373 Util.FireAndForget(HandleUseCircuitCode, new object[] { endPoint, packet });
1374 FreeUDPBuffer(buffer); 1374 FreeUDPBuffer(buffer);
1375 SendAckImmediate(endPoint, packet.Header.Sequence);
1375 return; 1376 return;
1376 } 1377 }
1377 } 1378 }
@@ -1720,11 +1721,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1720 1721
1721 queue = null; 1722 queue = null;
1722 1723
1723 // Send ack straight away to let the viewer know that the connection is active.
1724 // The client will be null if it already exists (e.g. if on a region crossing the client sends a use
1725 // circuit code to the existing child agent. This is not particularly obvious.
1726 SendAckImmediate(endPoint, uccp.Header.Sequence);
1727
1728 if (client != null) 1724 if (client != null)
1729 { 1725 {
1730 client.SendRegionHandshake(); 1726 client.SendRegionHandshake();