aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ClientStack
diff options
context:
space:
mode:
authorMW2008-07-21 15:13:34 +0000
committerMW2008-07-21 15:13:34 +0000
commitfb096dfbd54cfbcfa60be872cee1680eb521dd14 (patch)
treefdc6c3b4d5d15dfd03dc24138ada668e62af7562 /OpenSim/Region/ClientStack
parentMantis#1801. Thank you kindly, Junta_Kohime for a patch that: (diff)
downloadopensim-SC_OLD-fb096dfbd54cfbcfa60be872cee1680eb521dd14.zip
opensim-SC_OLD-fb096dfbd54cfbcfa60be872cee1680eb521dd14.tar.gz
opensim-SC_OLD-fb096dfbd54cfbcfa60be872cee1680eb521dd14.tar.bz2
opensim-SC_OLD-fb096dfbd54cfbcfa60be872cee1680eb521dd14.tar.xz
added experimental packet tracker (LLPacketTracker.cs), which can be told to track a packet and if it hasn't been acked within a set time, trigger a IClientAPI event, that the application/scene can handle. Currently only terrain packet tracking is finished, Tracking for initial Prim packets (first full update for a prim) is being worked on. Future improvements would be to make it a more generic packet tracker with callback delegates instead of events.
Add a test event handler (which would fire after a minute if a terrain packet hadn't been acked) to scene to handle the OnUnackedTerrain event, which currently just resends the terrain patch. The idea of this packet tracking is for the region level application to be able to know if the client stack gave up on sending a packet.
Diffstat (limited to 'OpenSim/Region/ClientStack')
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs115
-rw-r--r--OpenSim/Region/ClientStack/LindenUDP/LLPacketTracker.cs234
2 files changed, 309 insertions, 40 deletions
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
index 2c05097..c9fc83a 100644
--- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs
@@ -118,6 +118,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
118 118
119 private Dictionary<string, LLUUID> m_defaultAnimations = new Dictionary<string, LLUUID>(); 119 private Dictionary<string, LLUUID> m_defaultAnimations = new Dictionary<string, LLUUID>();
120 120
121 private LLPacketTracker m_packetTracker;
122
121 /* protected variables */ 123 /* protected variables */
122 124
123 protected static Dictionary<PacketType, PacketMethod> PacketHandlers = 125 protected static Dictionary<PacketType, PacketMethod> PacketHandlers =
@@ -282,6 +284,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
282 private SetScriptRunning handlerSetScriptRunning = null; 284 private SetScriptRunning handlerSetScriptRunning = null;
283 private UpdateVector handlerAutoPilotGo = null; 285 private UpdateVector handlerAutoPilotGo = null;
284 286
287 private TerrainUnacked handlerUnackedTerrain = null;
288
289 //**
290
285 /* Properties */ 291 /* Properties */
286 292
287 public LLUUID SecureSessionId 293 public LLUUID SecureSessionId
@@ -377,6 +383,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
377 public LLClientView(EndPoint remoteEP, IScene scene, AssetCache assetCache, LLPacketServer packServer, 383 public LLClientView(EndPoint remoteEP, IScene scene, AssetCache assetCache, LLPacketServer packServer,
378 AgentCircuitManager authenSessions, LLUUID agentId, LLUUID sessionId, uint circuitCode, EndPoint proxyEP) 384 AgentCircuitManager authenSessions, LLUUID agentId, LLUUID sessionId, uint circuitCode, EndPoint proxyEP)
379 { 385 {
386 m_packetTracker = new LLPacketTracker(this);
387
380 m_moneyBalance = 1000; 388 m_moneyBalance = 1000;
381 389
382 m_channelVersion = Helpers.StringToField(scene.GetSimulatorVersion()); 390 m_channelVersion = Helpers.StringToField(scene.GetSimulatorVersion());
@@ -410,6 +418,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
410 418
411 RegisterLocalPacketHandlers(); 419 RegisterLocalPacketHandlers();
412 420
421
413 m_clientThread = new Thread(new ThreadStart(AuthUser)); 422 m_clientThread = new Thread(new ThreadStart(AuthUser));
414 m_clientThread.Name = "ClientThread"; 423 m_clientThread.Name = "ClientThread";
415 m_clientThread.IsBackground = true; 424 m_clientThread.IsBackground = true;
@@ -654,6 +663,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
654 663
655 # endregion 664 # endregion
656 665
666 protected int m_terrainCheckerCount = 0;
657 /// <summary> 667 /// <summary>
658 /// Event handler for check client timer 668 /// Event handler for check client timer
659 /// checks to ensure that the client is still connected 669 /// checks to ensure that the client is still connected
@@ -685,7 +695,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
685 m_probesWithNoIngressPackets = 0; 695 m_probesWithNoIngressPackets = 0;
686 m_lastPacketsReceived = m_packetsReceived; 696 m_lastPacketsReceived = m_packetsReceived;
687 } 697 }
698
688 //SendPacketStats(); 699 //SendPacketStats();
700 m_packetTracker.Process();
701
702 if (m_terrainCheckerCount >= 4)
703 {
704 m_packetTracker.TerrainPacketCheck();
705 // m_packetTracker.PrimPacketCheck();
706 m_terrainCheckerCount = -1;
707 }
708 m_terrainCheckerCount++;
689 } 709 }
690 710
691 # region Setup 711 # region Setup
@@ -934,6 +954,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
934 public event SetScriptRunning OnSetScriptRunning; 954 public event SetScriptRunning OnSetScriptRunning;
935 public event UpdateVector OnAutoPilotGo; 955 public event UpdateVector OnAutoPilotGo;
936 956
957 public event TerrainUnacked OnUnackedTerrain;
958
937 #region Scene/Avatar to Client 959 #region Scene/Avatar to Client
938 960
939 /// <summary> 961 /// <summary>
@@ -1083,29 +1105,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1083 public virtual void SendLayerData(float[] map) 1105 public virtual void SendLayerData(float[] map)
1084 { 1106 {
1085 ThreadPool.QueueUserWorkItem(new WaitCallback(DoSendLayerData), (object)map); 1107 ThreadPool.QueueUserWorkItem(new WaitCallback(DoSendLayerData), (object)map);
1086 //try 1108
1087 //{
1088 // int[] patches = new int[4];
1089
1090 // for (int y = 0; y < 16; y++)
1091 // {
1092 // for (int x = 0; x < 16; x += 4)
1093 // {
1094 // patches[0] = x + 0 + y * 16;
1095 // patches[1] = x + 1 + y * 16;
1096 // patches[2] = x + 2 + y * 16;
1097 // patches[3] = x + 3 + y * 16;
1098
1099 // Packet layerpack = LLClientView.TerrainManager.CreateLandPacket(map, patches);
1100 // OutPacket(layerpack, ThrottleOutPacketType.Land);
1101 // }
1102 // }
1103 //}
1104 //catch (Exception e)
1105 //{
1106 // m_log.Warn("[client]: " +
1107 // "ClientView.API.cs: SendLayerData() - Failed with exception " + e.ToString());
1108 //}
1109 } 1109 }
1110 1110
1111 /// <summary> 1111 /// <summary>
@@ -1166,6 +1166,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1166 /// <param name="map">heightmap</param> 1166 /// <param name="map">heightmap</param>
1167 public void SendLayerData(int px, int py, float[] map) 1167 public void SendLayerData(int px, int py, float[] map)
1168 { 1168 {
1169 SendLayerData(px, py, map, true);
1170 }
1171
1172 public void SendLayerData(int px, int py, float[] map, bool track)
1173 {
1169 try 1174 try
1170 { 1175 {
1171 int[] patches = new int[1]; 1176 int[] patches = new int[1];
@@ -1177,6 +1182,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
1177 1182
1178 LayerDataPacket layerpack = LLClientView.TerrainManager.CreateLandPacket(map, patches); 1183 LayerDataPacket layerpack = LLClientView.TerrainManager.CreateLandPacket(map, patches);
1179 layerpack.Header.Zerocoded = true; 1184 layerpack.Header.Zerocoded = true;
1185
1186 if (track)
1187 {
1188 layerpack.Header.Sequence = NextSeqNum();
1189 m_packetTracker.TrackTerrainPacket(layerpack.Header.Sequence, px, py);
1190 }
1191
1180 OutPacket(layerpack, ThrottleOutPacketType.Land); 1192 OutPacket(layerpack, ThrottleOutPacketType.Land);
1181 } 1193 }
1182 catch (Exception e) 1194 catch (Exception e)
@@ -2297,14 +2309,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2297 ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, 2309 ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape,
2298 LLVector3 pos, LLVector3 vel, LLVector3 acc, LLQuaternion rotation, LLVector3 rvel, 2310 LLVector3 pos, LLVector3 vel, LLVector3 acc, LLQuaternion rotation, LLVector3 rvel,
2299 uint flags, LLUUID objectID, LLUUID ownerID, string text, byte[] color, 2311 uint flags, LLUUID objectID, LLUUID ownerID, string text, byte[] color,
2300 uint parentID, byte[] particleSystem, byte clickAction) 2312 uint parentID, byte[] particleSystem, byte clickAction, bool track)
2301 { 2313 {
2302 byte[] textureanim = new byte[0]; 2314 byte[] textureanim = new byte[0];
2303 2315
2304 SendPrimitiveToClient(regionHandle, timeDilation, localID, primShape, pos, vel, 2316 SendPrimitiveToClient(regionHandle, timeDilation, localID, primShape, pos, vel,
2305 acc, rotation, rvel, flags, 2317 acc, rotation, rvel, flags,
2306 objectID, ownerID, text, color, parentID, particleSystem, 2318 objectID, ownerID, text, color, parentID, particleSystem,
2307 clickAction, textureanim, false, (uint)0, LLUUID.Zero, LLUUID.Zero, 0, 0, 0); 2319 clickAction, textureanim, false, (uint)0, LLUUID.Zero, LLUUID.Zero, 0, 0, 0, track);
2308 } 2320 }
2309 2321
2310 public void SendPrimitiveToClient( 2322 public void SendPrimitiveToClient(
@@ -2312,8 +2324,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2312 LLVector3 pos, LLVector3 velocity, LLVector3 acceleration, LLQuaternion rotation, LLVector3 rotational_velocity, 2324 LLVector3 pos, LLVector3 velocity, LLVector3 acceleration, LLQuaternion rotation, LLVector3 rotational_velocity,
2313 uint flags, 2325 uint flags,
2314 LLUUID objectID, LLUUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, 2326 LLUUID objectID, LLUUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem,
2315 byte clickAction, byte[] textureanim, bool attachment, uint AttachPoint, LLUUID AssetId, LLUUID SoundId, double SoundGain, byte SoundFlags, double SoundRadius) 2327 byte clickAction, byte[] textureanim, bool attachment, uint AttachPoint, LLUUID AssetId, LLUUID SoundId, double SoundGain, byte SoundFlags, double SoundRadius, bool track)
2316 { 2328 {
2329
2317 if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) 2330 if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0)
2318 rotation = LLQuaternion.Identity; 2331 rotation = LLQuaternion.Identity;
2319 2332
@@ -2396,6 +2409,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
2396 outPacket.ObjectData[0].TextureAnim = textureanim; 2409 outPacket.ObjectData[0].TextureAnim = textureanim;
2397 } 2410 }
2398 outPacket.Header.Zerocoded = true; 2411 outPacket.Header.Zerocoded = true;
2412
2413 if (track)
2414 {
2415 outPacket.Header.Sequence = NextSeqNum();
2416 m_packetTracker.TrackPrimPacket(outPacket.Header.Sequence, objectID);
2417 }
2418
2399 OutPacket(outPacket, ThrottleOutPacketType.Task); 2419 OutPacket(outPacket, ThrottleOutPacketType.Task);
2400 } 2420 }
2401 2421
@@ -3816,7 +3836,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3816 3836
3817 if (!Pack.Header.Resent) 3837 if (!Pack.Header.Resent)
3818 { 3838 {
3819 Pack.Header.Sequence = NextSeqNum(); 3839 if (Pack.Header.Sequence == 0)
3840 {
3841 Pack.Header.Sequence = NextSeqNum();
3842 }
3820 3843
3821 if (Pack.Header.Reliable) //DIRTY HACK 3844 if (Pack.Header.Reliable) //DIRTY HACK
3822 { 3845 {
@@ -3878,13 +3901,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3878 { 3901 {
3879 foreach (uint ackedPacketId in NewPack.Header.AckList) 3902 foreach (uint ackedPacketId in NewPack.Header.AckList)
3880 { 3903 {
3881 Packet ackedPacket; 3904 RemovePacketFromNeedAckList(ackedPacketId);
3882
3883 if (m_needAck.TryGetValue(ackedPacketId, out ackedPacket))
3884 {
3885 m_unAckedBytes -= ackedPacket.ToBytes().Length;
3886 m_needAck.Remove(ackedPacketId);
3887 }
3888 } 3905 }
3889 } 3906 }
3890 } 3907 }
@@ -3899,12 +3916,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3899 foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets) 3916 foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets)
3900 { 3917 {
3901 uint ackedPackId = block.ID; 3918 uint ackedPackId = block.ID;
3902 Packet ackedPacket; 3919 RemovePacketFromNeedAckList(ackedPackId);
3903 if (m_needAck.TryGetValue(ackedPackId, out ackedPacket))
3904 {
3905 m_unAckedBytes -= ackedPacket.ToBytes().Length;
3906 m_needAck.Remove(ackedPackId);
3907 }
3908 } 3920 }
3909 } 3921 }
3910 } 3922 }
@@ -3926,6 +3938,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP
3926 } 3938 }
3927 } 3939 }
3928 3940
3941 private void RemovePacketFromNeedAckList(uint ackedPackId)
3942 {
3943 Packet ackedPacket;
3944 if (m_needAck.TryGetValue(ackedPackId, out ackedPacket))
3945 {
3946 m_unAckedBytes -= ackedPacket.ToBytes().Length;
3947 m_needAck.Remove(ackedPackId);
3948
3949 m_packetTracker.PacketAck(ackedPackId);
3950 }
3951 }
3952
3929 /// <summary> 3953 /// <summary>
3930 /// The dreaded OutPacket. This should only be called from withink the ClientStack itself right now 3954 /// The dreaded OutPacket. This should only be called from withink the ClientStack itself right now
3931 /// This is the entry point for simulator packets to go out to the client. 3955 /// This is the entry point for simulator packets to go out to the client.
@@ -4037,6 +4061,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4037 SendAcks(); 4061 SendAcks();
4038 ResendUnacked(); 4062 ResendUnacked();
4039 SendPacketStats(); 4063 SendPacketStats();
4064 // TerrainPacketTrack();
4040 } 4065 }
4041 4066
4042 /// <summary> 4067 /// <summary>
@@ -4053,6 +4078,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4053 } 4078 }
4054 } 4079 }
4055 4080
4081
4056 /// <summary> 4082 /// <summary>
4057 /// Emties out the old packets in the packet duplication tracking table. 4083 /// Emties out the old packets in the packet duplication tracking table.
4058 /// </summary> 4084 /// </summary>
@@ -4092,6 +4118,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP
4092 4118
4093 #endregion 4119 #endregion
4094 4120
4121 public void TriggerTerrainUnackedEvent(int patchX, int patchY)
4122 {
4123 handlerUnackedTerrain = OnUnackedTerrain;
4124 if (handlerUnackedTerrain != null)
4125 {
4126 handlerUnackedTerrain(this, patchX, patchY);
4127 }
4128 }
4129
4095 // Previously ClientView.ProcessPackets 4130 // Previously ClientView.ProcessPackets
4096 4131
4097 public bool AddMoney(int debit) 4132 public bool AddMoney(int debit)
diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketTracker.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketTracker.cs
new file mode 100644
index 0000000..e775a67
--- /dev/null
+++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketTracker.cs
@@ -0,0 +1,234 @@
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading;
5using libsecondlife;
6
7namespace OpenSim.Region.ClientStack.LindenUDP
8{
9
10 public class LLPacketTracker
11 {
12 public delegate void PacketAcked(uint sequenceNumber);
13 public event PacketAcked OnPacketAcked;
14
15 protected List<uint> m_beenAcked = new List<uint>();
16
17 protected TerrainPacketTracker[,] m_sentTerrainPackets = new TerrainPacketTracker[16, 16];
18 protected Dictionary<LLUUID, PrimPacketTracker> m_sendPrimPackets = new Dictionary<LLUUID, PrimPacketTracker>();
19
20 protected LLClientView m_parentClient;
21
22 public LLPacketTracker(LLClientView parent)
23 {
24 m_parentClient = parent;
25 OnPacketAcked += TerrainPacketAcked;
26 //OnPacketAcked += PrimPacketAcked;
27 }
28
29 public void PacketAck(uint sequenceNumber)
30 {
31 lock (m_beenAcked)
32 {
33 m_beenAcked.Add(sequenceNumber);
34 }
35 }
36
37 public void TrackTerrainPacket(uint sequenceNumber, int patchX, int patchY)
38 {
39 TerrainPacketTracker tracker = new TerrainPacketTracker();
40 tracker.X = patchX;
41 tracker.Y = patchY;
42 tracker.SeqNumber = sequenceNumber;
43 tracker.TimeSent = DateTime.Now;
44 lock (m_sentTerrainPackets)
45 {
46 m_sentTerrainPackets[patchX, patchY] = tracker;
47 }
48 }
49
50 public void TrackPrimPacket(uint sequenceNumber, LLUUID primID)
51 {
52 PrimPacketTracker tracker = new PrimPacketTracker();
53 tracker.PrimID = primID;
54 tracker.TimeSent = DateTime.Now;
55 tracker.SeqNumber = sequenceNumber;
56 lock (m_sendPrimPackets)
57 {
58 m_sendPrimPackets[primID] = tracker;
59 }
60 }
61
62 public void TerrainPacketCheck()
63 {
64 DateTime now = DateTime.Now;
65 List<TerrainPacketTracker> resendList = new List<TerrainPacketTracker>();
66 lock (m_sentTerrainPackets)
67 {
68 for (int y = 0; y < 16; y++)
69 {
70 for (int x = 0; x < 16; x++)
71 {
72 if (m_sentTerrainPackets[x, y] != null)
73 {
74 TerrainPacketTracker tracker = m_sentTerrainPackets[x, y];
75 if ((now - tracker.TimeSent) > TimeSpan.FromMinutes(1))
76 {
77 tracker.TimeSent = now;
78 m_sentTerrainPackets[x, y] = null;
79 resendList.Add(tracker);
80 }
81 }
82 }
83 }
84 }
85
86 foreach (TerrainPacketTracker tracker in resendList)
87 {
88 m_parentClient.TriggerTerrainUnackedEvent(tracker.X, tracker.Y);
89 }
90 }
91
92 public void PrimPacketCheck()
93 {
94 DateTime now = DateTime.Now;
95 List<PrimPacketTracker> resendList = new List<PrimPacketTracker>();
96 List<PrimPacketTracker> ackedList = new List<PrimPacketTracker>();
97
98 lock (m_sendPrimPackets)
99 {
100 foreach (PrimPacketTracker tracker in m_sendPrimPackets.Values)
101 {
102 if (tracker.Acked)
103 {
104 ackedList.Add(tracker);
105 }
106 else if (((now - tracker.TimeSent) > TimeSpan.FromMinutes(1)) && (!tracker.Acked))
107 {
108 resendList.Add(tracker);
109 }
110 }
111 }
112
113 foreach (PrimPacketTracker tracker in resendList)
114 {
115 lock (m_sendPrimPackets)
116 {
117 m_sendPrimPackets.Remove(tracker.PrimID);
118 }
119 //call event
120 Console.WriteLine("Prim packet not acked, " + tracker.PrimID.ToString());
121 }
122
123
124 RemovePrimTrackers(ackedList);
125 }
126
127 public void PrimTrackerCleanup()
128 {
129 List<PrimPacketTracker> ackedList = new List<PrimPacketTracker>();
130
131 lock (m_sendPrimPackets)
132 {
133 foreach (PrimPacketTracker tracker in m_sendPrimPackets.Values)
134 {
135 if (tracker.Acked)
136 {
137 ackedList.Add(tracker);
138 }
139 }
140 }
141 Thread.Sleep(15); //give a little bit of time for other code to access list before we lock it again
142
143 RemovePrimTrackers(ackedList);
144 }
145
146 protected void RemovePrimTrackers(List<PrimPacketTracker> ackedList)
147 {
148 lock (m_sendPrimPackets)
149 {
150 foreach (PrimPacketTracker tracker in ackedList)
151 {
152 m_sendPrimPackets.Remove(tracker.PrimID);
153 }
154 }
155 }
156
157 protected void TerrainPacketAcked(uint sequence)
158 {
159 lock (m_sentTerrainPackets)
160 {
161 for (int y = 0; y < 16; y++)
162 {
163 for (int x = 0; x < 16; x++)
164 {
165 if (m_sentTerrainPackets[x, y] != null)
166 {
167 if (m_sentTerrainPackets[x, y].SeqNumber == sequence)
168 {
169 m_sentTerrainPackets[x, y] = null;
170 return;
171 }
172 }
173 }
174 }
175 }
176 }
177
178 protected void PrimPacketAcked(uint sequence)
179 {
180 lock (m_sendPrimPackets)
181 {
182 foreach (PrimPacketTracker tracker in m_sendPrimPackets.Values)
183 {
184 if (tracker.SeqNumber == sequence)
185 {
186 tracker.Acked = true;
187 break;
188 }
189 }
190 }
191 }
192
193 public void Process()
194 {
195 List<uint> ackedPackets = null;
196 lock (m_beenAcked)
197 {
198 ackedPackets = new List<uint>(m_beenAcked);
199 m_beenAcked.Clear();
200 }
201
202 if (ackedPackets != null)
203 {
204 foreach (uint packetId in ackedPackets)
205 {
206 if (OnPacketAcked != null)
207 {
208 OnPacketAcked(packetId);
209 }
210 }
211 }
212
213 // ackedPackets.Clear();
214 ackedPackets = null;
215 }
216
217 public class TerrainPacketTracker
218 {
219 public uint SeqNumber = 0;
220 public int X;
221 public int Y;
222 public DateTime TimeSent;
223
224 }
225
226 public class PrimPacketTracker
227 {
228 public uint SeqNumber = 0;
229 public DateTime TimeSent;
230 public LLUUID PrimID;
231 public bool Acked = false;
232 }
233 }
234}