diff --git a/OpenSim/Framework/BlockingQueue.cs b/OpenSim/Framework/BlockingQueue.cs index 345b361eb1..5b7e9118a6 100644 --- a/OpenSim/Framework/BlockingQueue.cs +++ b/OpenSim/Framework/BlockingQueue.cs @@ -32,9 +32,19 @@ namespace OpenSim.Framework { public class BlockingQueue { + private readonly Queue m_pqueue = new Queue(); private readonly Queue m_queue = new Queue(); private readonly object m_queueSync = new object(); + public void PriorityEnqueue(T value) + { + lock (m_queueSync) + { + m_pqueue.Enqueue(value); + Monitor.Pulse(m_queueSync); + } + } + public void Enqueue(T value) { lock (m_queueSync) @@ -48,11 +58,13 @@ namespace OpenSim.Framework { lock (m_queueSync) { - if (m_queue.Count < 1) + if (m_queue.Count < 1 && m_pqueue.Count < 1) { Monitor.Wait(m_queueSync); } + if(m_pqueue.Count > 0) + return m_pqueue.Dequeue(); return m_queue.Dequeue(); } } @@ -61,6 +73,8 @@ namespace OpenSim.Framework { lock (m_queueSync) { + if(m_pqueue.Contains(item)) + return true; return m_queue.Contains(item); } } @@ -69,7 +83,7 @@ namespace OpenSim.Framework { lock (m_queueSync) { - return m_queue.Count; + return m_queue.Count+m_pqueue.Count; } } @@ -81,4 +95,4 @@ namespace OpenSim.Framework } } } -} \ No newline at end of file +} diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index a835598a1e..b1f62f1d31 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -254,8 +254,6 @@ namespace OpenSim.Framework public delegate void FriendshipTermination(IClientAPI remoteClient, LLUUID agentID, LLUUID ExID); - public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); - public delegate void MoneyTransferRequest(LLUUID sourceID, LLUUID destID, int amount, int transactionType, string description); public delegate void ParcelBuy(LLUUID agentId, LLUUID groupId, bool final, bool groupOwned, @@ -324,6 +322,8 @@ namespace OpenSim.Framework string LastName { get; } + IScene Scene { get; } + // [Obsolete("LLClientView Specific - Replace with ???")] int NextAnimationSequenceNumber { get; } @@ -460,7 +460,6 @@ namespace OpenSim.Framework event FriendActionDelegate OnApproveFriendRequest; event FriendActionDelegate OnDenyFriendRequest; event FriendshipTermination OnTerminateFriendship; - event PacketStats OnPacketStats; // Financial packets event MoneyTransferRequest OnMoneyTransferRequest; @@ -526,7 +525,6 @@ namespace OpenSim.Framework void SendLayerData(float[] map); void SendLayerData(int px, int py, float[] map); - void SendLayerData(int px, int py, float[] map, bool track); void MoveAgentIntoRegion(RegionInfo regInfo, LLVector3 pos, LLVector3 look); void InformClientOfNeighbour(ulong neighbourHandle, IPEndPoint neighbourExternalEndPoint); @@ -561,13 +559,14 @@ namespace OpenSim.Framework LLVector3 pos, LLVector3 vel, LLVector3 acc, LLQuaternion rotation, LLVector3 rvel, uint flags, LLUUID objectID, LLUUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, - byte clickAction, byte[] textureanim, bool attachment, uint AttachPoint, LLUUID AssetId, LLUUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius, bool track); + byte clickAction, byte[] textureanim, bool attachment, uint AttachPoint, LLUUID AssetId, LLUUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius); void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, LLVector3 pos, LLVector3 vel, LLVector3 acc, LLQuaternion rotation, LLVector3 rvel, uint flags, LLUUID objectID, LLUUID ownerID, string text, byte[] color, - uint parentID, byte[] particleSystem, byte clickAction, bool track); + uint parentID, byte[] particleSystem, byte clickAction); + void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, LLVector3 position, LLQuaternion rotation, LLVector3 velocity, LLVector3 rotationalvelocity, byte state, LLUUID AssetId); @@ -703,6 +702,7 @@ namespace OpenSim.Framework void SetDebug(int newDebug); void InPacket(Packet NewPack); + void ProcessInPacket(Packet NewPack); void Close(bool ShutdownCircuit); void Kick(string message); void Stop(); diff --git a/OpenSim/Framework/ThrottleOutPacketType.cs b/OpenSim/Framework/ThrottleOutPacketType.cs index e045783156..27b25d2843 100644 --- a/OpenSim/Framework/ThrottleOutPacketType.cs +++ b/OpenSim/Framework/ThrottleOutPacketType.cs @@ -10,6 +10,7 @@ namespace OpenSim.Framework Texture = 5, Asset = 6, Unknown = 7, // Also doubles as 'do not throttle' - Back = 8 + Back = 8, + LowpriorityTask = 9 } -} \ No newline at end of file +} diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs index c9fc83a4c8..41ac657dfa 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLClientView.cs @@ -49,20 +49,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public delegate bool PacketMethod(IClientAPI simClient, Packet packet); - /// - /// Class that keeps track of past packets so that they don't get - /// duplicated when the client doesn't get back an ack - /// - public class PacketDupeLimiter - { - public PacketType pktype; - public int timeIn; - public uint packetId; - public PacketDupeLimiter() - { - } - } - /// /// Handles new client connections /// Constructor takes a single Packet and authenticates everything @@ -79,7 +65,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /* static variables */ public static TerrainManager TerrainManager = new TerrainManager(new SecondLife()); - public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, LLUUID agentID, ThrottleOutPacketType throttlePacketType); public static SynchronizeClientHandler SynchronizeClient = null; /* private variables */ private readonly LLUUID m_sessionId; @@ -93,15 +78,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP private bool m_clientBlocked = false; - // for sim stats - private int m_packetsReceived = 0; - private int m_lastPacketsReceivedSentToScene = 0; - private int m_unAckedBytes = 0; - - private int m_packetsSent = 0; - private int m_lastPacketsSentSentToScene = 0; - private int m_clearDuplicatePacketTrackingOlderThenXSeconds = 30; - private int m_probesWithNoIngressPackets = 0; private int m_lastPacketsReceived = 0; private byte[] ZeroOutBuffer = new byte[4096]; @@ -109,8 +85,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private readonly LLUUID m_agentId; private readonly uint m_circuitCode; private int m_moneyBalance; - - private Dictionary m_dupeLimiter = new Dictionary(); + private IPacketHandler m_PacketHandler; private int m_animationSequenceNumber = 1; @@ -118,8 +93,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP private Dictionary m_defaultAnimations = new Dictionary(); - private LLPacketTracker m_packetTracker; - /* protected variables */ protected static Dictionary PacketHandlers = @@ -130,17 +103,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected IScene m_scene; protected AgentCircuitManager m_authenticateSessionsHandler; - protected LLPacketQueue m_packetQueue; - - protected Dictionary m_pendingAcks = new Dictionary(); - protected Dictionary m_needAck = new Dictionary(); - - protected Timer m_ackTimer; - protected uint m_sequence = 0; - protected object m_sequenceLock = new object(); - protected const int MAX_APPENDED_ACKS = 10; - protected const int RESEND_TIMEOUT = 4000; - protected const int MAX_SEQUENCE = 0xFFFFFF; protected LLPacketServer m_networkServer; /* public variables */ @@ -263,7 +225,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP private UpdateVector handlerUpdateVector = null; //OnUpdatePrimGroupPosition; private UpdatePrimRotation handlerUpdatePrimRotation = null; //OnUpdatePrimGroupRotation; // private UpdatePrimGroupRotation handlerUpdatePrimGroupRotation = null; //OnUpdatePrimGroupMouseRotation; - private PacketStats handlerPacketStats = null; // OnPacketStats;# // private RequestAsset handlerRequestAsset = null; // OnRequestAsset; private UUIDNameRequest handlerTeleportHomeRequest = null; @@ -378,13 +339,24 @@ namespace OpenSim.Region.ClientStack.LindenUDP get { return m_animationSequenceNumber++; } } + public IPacketHandler PacketHandler + { + get { return m_PacketHandler; } + } + + bool m_IsActive = true; + + public bool IsActive + { + get { return m_IsActive; } + set { m_IsActive = value; } + } + /* METHODS */ public LLClientView(EndPoint remoteEP, IScene scene, AssetCache assetCache, LLPacketServer packServer, AgentCircuitManager authenSessions, LLUUID agentId, LLUUID sessionId, uint circuitCode, EndPoint proxyEP) { - m_packetTracker = new LLPacketTracker(this); - m_moneyBalance = 1000; m_channelVersion = Helpers.StringToField(scene.GetSimulatorVersion()); @@ -414,7 +386,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // in it to process. It's an on-purpose threadlock though because // without it, the clientloop will suck up all sim resources. - m_packetQueue = new LLPacketQueue(agentId); + m_PacketHandler = new LLPacketHandler(this); + m_PacketHandler.SynchronizeClient = SynchronizeClient; RegisterLocalPacketHandlers(); @@ -423,7 +396,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_clientThread.Name = "ClientThread"; m_clientThread.IsBackground = true; m_clientThread.Start(); - ThreadTracker.Add(m_clientThread); } public void SetDebug(int newDebug) @@ -444,12 +416,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP DisableSimulatorPacket disable = (DisableSimulatorPacket)PacketPool.Instance.GetPacket(PacketType.DisableSimulator); OutPacket(disable, ThrottleOutPacketType.Unknown); - m_packetQueue.Close(); - Thread.Sleep(2000); // Shut down timers - m_ackTimer.Stop(); m_clientPingTimer.Stop(); // This is just to give the client a reasonable chance of @@ -479,7 +448,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // Pull Client out of Region m_log.Info("[CLIENT]: Close has been called"); - m_packetQueue.Flush(); + m_PacketHandler.Flush(); //raiseevent on the packet server to Shutdown the circuit if (shutdownCircuit) @@ -509,20 +478,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void Stop() { // Shut down timers - m_ackTimer.Stop(); m_clientPingTimer.Stop(); } public void Restart() { // re-construct - m_pendingAcks = new Dictionary(); - m_needAck = new Dictionary(); - m_sequence += 1000000; - - m_ackTimer = new Timer(750); - m_ackTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed); - m_ackTimer.Start(); + m_PacketHandler.Clear(); m_clientPingTimer = new Timer(5000); m_clientPingTimer.Elapsed += new ElapsedEventHandler(CheckClientConnectivity); @@ -531,8 +493,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void Terminate() { - // disable blocking queue - m_packetQueue.Enqueue(null); + m_PacketHandler.Stop(); // wait for thread stoped m_clientThread.Join(); @@ -638,7 +599,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.Info("[CLIENT]: Entered loop"); while (true) { - LLQueItem nextPacket = m_packetQueue.Dequeue(); + LLQueItem nextPacket = m_PacketHandler.PacketQueue.Dequeue(); if (nextPacket == null) { m_log.Error("Got a NULL packet in Client Loop, bailing out of our client loop"); @@ -646,12 +607,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP } if (nextPacket.Incoming) { - if (nextPacket.Packet.Type != PacketType.AgentUpdate) - { - m_packetsReceived++; - } DebugPacket("IN", nextPacket.Packet); - ProcessInPacket(nextPacket.Packet); + m_PacketHandler.ProcessInPacket(nextPacket.Packet); } else { @@ -672,7 +629,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// protected void CheckClientConnectivity(object sender, ElapsedEventArgs e) { - if (m_packetsReceived == m_lastPacketsReceived) + if (m_PacketHandler.PacketsReceived == m_PacketHandler.PacketsReceivedReported) { m_probesWithNoIngressPackets++; if ((m_probesWithNoIngressPackets > 30 && !m_clientBlocked) || (m_probesWithNoIngressPackets > 90 && m_clientBlocked)) @@ -693,19 +650,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // Something received in the meantime - we can reset the counters m_probesWithNoIngressPackets = 0; - m_lastPacketsReceived = m_packetsReceived; } - //SendPacketStats(); - m_packetTracker.Process(); - - if (m_terrainCheckerCount >= 4) - { - m_packetTracker.TerrainPacketCheck(); - // m_packetTracker.PrimPacketCheck(); - m_terrainCheckerCount = -1; - } - m_terrainCheckerCount++; } # region Setup @@ -719,9 +665,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP //this.UploadAssets = new AgentAssetUpload(this, m_assetCache, m_inventoryCache); // Establish our two timers. We could probably get this down to one - m_ackTimer = new Timer(750); - m_ackTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed); - m_ackTimer.Start(); m_clientPingTimer = new Timer(5000); m_clientPingTimer.Elapsed += new ElapsedEventHandler(CheckClientConnectivity); @@ -754,7 +697,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP "[CLIENT]: New user request denied to avatar {0} connecting with circuit code {1} from {2}", m_agentId, m_circuitCode, m_userEndPoint); - m_packetQueue.Close(); + m_PacketHandler.Stop(); m_clientThread.Abort(); } else @@ -920,7 +863,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public event FriendActionDelegate OnApproveFriendRequest; public event FriendActionDelegate OnDenyFriendRequest; public event FriendshipTermination OnTerminateFriendship; - public event PacketStats OnPacketStats; public event MoneyTransferRequest OnMoneyTransferRequest; public event EconomyDataRequest OnEconomyDataRequest; public event MoneyBalanceRequest OnMoneyBalanceRequest; @@ -1105,7 +1047,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public virtual void SendLayerData(float[] map) { ThreadPool.QueueUserWorkItem(new WaitCallback(DoSendLayerData), (object)map); - } /// @@ -1165,11 +1106,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Patch coordinate (y) 0..15 /// heightmap public void SendLayerData(int px, int py, float[] map) - { - SendLayerData(px, py, map, true); - } - - public void SendLayerData(int px, int py, float[] map, bool track) { try { @@ -1183,12 +1119,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP LayerDataPacket layerpack = LLClientView.TerrainManager.CreateLandPacket(map, patches); layerpack.Header.Zerocoded = true; - if (track) - { - layerpack.Header.Sequence = NextSeqNum(); - m_packetTracker.TrackTerrainPacket(layerpack.Header.Sequence, px, py); - } - OutPacket(layerpack, ThrottleOutPacketType.Land); } catch (Exception e) @@ -2309,14 +2239,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP ulong regionHandle, ushort timeDilation, uint localID, PrimitiveBaseShape primShape, LLVector3 pos, LLVector3 vel, LLVector3 acc, LLQuaternion rotation, LLVector3 rvel, uint flags, LLUUID objectID, LLUUID ownerID, string text, byte[] color, - uint parentID, byte[] particleSystem, byte clickAction, bool track) + uint parentID, byte[] particleSystem, byte clickAction) { byte[] textureanim = new byte[0]; SendPrimitiveToClient(regionHandle, timeDilation, localID, primShape, pos, vel, acc, rotation, rvel, flags, objectID, ownerID, text, color, parentID, particleSystem, - clickAction, textureanim, false, (uint)0, LLUUID.Zero, LLUUID.Zero, 0, 0, 0, track); + clickAction, textureanim, false, (uint)0, LLUUID.Zero, LLUUID.Zero, 0, 0, 0); } public void SendPrimitiveToClient( @@ -2324,7 +2254,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP LLVector3 pos, LLVector3 velocity, LLVector3 acceleration, LLQuaternion rotation, LLVector3 rotational_velocity, uint flags, LLUUID objectID, LLUUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, - byte clickAction, byte[] textureanim, bool attachment, uint AttachPoint, LLUUID AssetId, LLUUID SoundId, double SoundGain, byte SoundFlags, double SoundRadius, bool track) + byte clickAction, byte[] textureanim, bool attachment, uint AttachPoint, LLUUID AssetId, LLUUID SoundId, double SoundGain, byte SoundFlags, double SoundRadius) { if (rotation.X == rotation.Y && rotation.Y == rotation.Z && rotation.Z == rotation.W && rotation.W == 0) @@ -2410,13 +2340,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } outPacket.Header.Zerocoded = true; - if (track) - { - outPacket.Header.Sequence = NextSeqNum(); - m_packetTracker.TrackPrimPacket(outPacket.Header.Sequence, objectID); - } - - OutPacket(outPacket, ThrottleOutPacketType.Task); + OutPacket(outPacket, ThrottleOutPacketType.LowpriorityTask); } /// @@ -2440,7 +2364,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP terse.ObjectData[0] = CreatePrimImprovedBlock(localID, position, rotation, velocity, rotationalvelocity, state); // AssetID should fall into here probably somehow... terse.Header.Reliable = false; terse.Header.Zerocoded = true; - OutPacket(terse, ThrottleOutPacketType.Task); + OutPacket(terse, ThrottleOutPacketType.LowpriorityTask); } public void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, LLVector3 position, @@ -2456,7 +2380,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP terse.ObjectData[0] = CreatePrimImprovedBlock(localID, position, rotation, velocity, rotationalvelocity, 0); terse.Header.Reliable = false; terse.Header.Zerocoded = true; - OutPacket(terse, ThrottleOutPacketType.Task); + OutPacket(terse, ThrottleOutPacketType.LowpriorityTask); } public void SendAssetUploadCompleteMessage(sbyte AssetType, bool Success, LLUUID AssetFullID) @@ -3736,7 +3660,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public byte[] GetThrottlesPacked(float multiplier) { - return m_packetQueue.GetThrottlesPacked(multiplier); + return m_PacketHandler.PacketQueue.GetThrottlesPacked(multiplier); } /// /// sets the throttles from values supplied by the client @@ -3744,86 +3668,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public void SetChildAgentThrottle(byte[] throttles) { - m_packetQueue.SetThrottleFromClient(throttles); + m_PacketHandler.PacketQueue.SetThrottleFromClient(throttles); } // Previously ClientView.m_packetQueue - // A thread safe sequence number allocator. - protected uint NextSeqNum() - { - // Set the sequence number - uint seq = 1; - lock (m_sequenceLock) - { - if (m_sequence >= MAX_SEQUENCE) - { - m_sequence = 1; - } - else - { - m_sequence++; - } - seq = m_sequence; - } - return seq; - } - - protected void AddAck(Packet Pack) - { - lock (m_needAck) - { - if (!m_needAck.ContainsKey(Pack.Header.Sequence)) - { - try - { - m_needAck.Add(Pack.Header.Sequence, Pack); - m_unAckedBytes += Pack.ToBytes().Length; - } - //BUG: severity=major - This looks like a framework bug!? - catch (Exception) // HACKY - { - // Ignore - // Seems to throw a exception here occasionally - // of 'duplicate key' despite being locked. - // !?!?!? - } - } - else - { - // Client.Log("Attempted to add a duplicate sequence number (" + - // packet.Header.m_sequence + ") to the m_needAck dictionary for packet type " + - // packet.Type.ToString(), Helpers.LogLevel.Warning); - } - } - } - /// - /// Append any ACKs that need to be sent out to this packet - /// - /// - protected virtual void SetPendingAcks(ref Packet Pack) - { - - lock (m_pendingAcks) - { - // TODO: If we are over MAX_APPENDED_ACKS we should drain off some of these - if (m_pendingAcks.Count > 0 && m_pendingAcks.Count < MAX_APPENDED_ACKS) - { - Pack.Header.AckList = new uint[m_pendingAcks.Count]; - int i = 0; - - foreach (uint ack in m_pendingAcks.Values) - { - Pack.Header.AckList[i] = ack; - i++; - } - - m_pendingAcks.Clear(); - Pack.Header.AppendedAcks = true; - } - } - } - /// /// Helper routine to prepare the packet for sending to UDP client /// This converts it to bytes and puts it on the outgoing buffer @@ -3834,24 +3683,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Keep track of when this packet was sent out Pack.TickCount = System.Environment.TickCount; - if (!Pack.Header.Resent) - { - if (Pack.Header.Sequence == 0) - { - Pack.Header.Sequence = NextSeqNum(); - } - - if (Pack.Header.Reliable) //DIRTY HACK - { - AddAck(Pack); // this adds the need to ack this packet later - - if (Pack.Type != PacketType.PacketAck && Pack.Type != PacketType.LogoutRequest) - { - SetPendingAcks(ref Pack); - } - } - } - // Actually make the byte array and send it try { @@ -3886,249 +3717,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public virtual void InPacket(Packet NewPack) { - if (!m_packetProcessingEnabled && NewPack.Type != PacketType.LogoutRequest) - { - PacketPool.Instance.ReturnPacket(NewPack); - return; - } - - // Handle appended ACKs - if (NewPack != null) - { - if (NewPack.Header.AppendedAcks) - { - lock (m_needAck) - { - foreach (uint ackedPacketId in NewPack.Header.AckList) - { - RemovePacketFromNeedAckList(ackedPacketId); - } - } - } - - // Handle PacketAck packets - if (NewPack.Type == PacketType.PacketAck) - { - PacketAckPacket ackPacket = (PacketAckPacket)NewPack; - - lock (m_needAck) - { - foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets) - { - uint ackedPackId = block.ID; - RemovePacketFromNeedAckList(ackedPackId); - } - } - } - else if ((NewPack.Type == PacketType.StartPingCheck)) - { - //reply to pingcheck - StartPingCheckPacket startPing = (StartPingCheckPacket)NewPack; - CompletePingCheckPacket endPing = (CompletePingCheckPacket)PacketPool.Instance.GetPacket(PacketType.CompletePingCheck); - endPing.PingID.PingID = startPing.PingID.PingID; - OutPacket(endPing, ThrottleOutPacketType.Task); - } - else - { - LLQueItem item = new LLQueItem(); - item.Packet = NewPack; - item.Incoming = true; - m_packetQueue.Enqueue(item); - } - } - } - - private void RemovePacketFromNeedAckList(uint ackedPackId) - { - Packet ackedPacket; - if (m_needAck.TryGetValue(ackedPackId, out ackedPacket)) - { - m_unAckedBytes -= ackedPacket.ToBytes().Length; - m_needAck.Remove(ackedPackId); - - m_packetTracker.PacketAck(ackedPackId); - } + m_PacketHandler.InPacket(NewPack); } /// - /// The dreaded OutPacket. This should only be called from withink the ClientStack itself right now - /// This is the entry point for simulator packets to go out to the client. + /// The dreaded OutPacket. This should only be called from within + /// the ClientStack itself right now + /// This is the entry point for simulator packets to go out to + /// the client. /// /// /// Corresponds to the type of data that is going out. Enum public virtual void OutPacket(Packet NewPack, ThrottleOutPacketType throttlePacketType) { - if ((SynchronizeClient != null) && (!IsActive)) - { - // Sending packet to active client's server. - if (SynchronizeClient(m_scene, NewPack, m_agentId, throttlePacketType)) - { - return; - } - } - - LLQueItem item = new LLQueItem(); - item.Packet = NewPack; - item.Incoming = false; - item.throttleType = throttlePacketType; // Packet throttle type - m_packetQueue.Enqueue(item); - m_packetsSent++; + m_PacketHandler.OutPacket(NewPack, throttlePacketType); } - # region Low Level Packet Methods - - protected void ack_pack(Packet Pack) - { - if (Pack.Header.Reliable) - { - PacketAckPacket ack_it = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck); - // TODO: don't create new blocks if recycling an old packet - ack_it.Packets = new PacketAckPacket.PacketsBlock[1]; - ack_it.Packets[0] = new PacketAckPacket.PacketsBlock(); - ack_it.Packets[0].ID = Pack.Header.Sequence; - ack_it.Header.Reliable = false; - - OutPacket(ack_it, ThrottleOutPacketType.Unknown); - } - /* - if (Pack.Header.Reliable) - { - lock (m_pendingAcks) - { - uint sequence = (uint)Pack.Header.m_sequence; - if (!m_pendingAcks.ContainsKey(sequence)) { m_pendingAcks[sequence] = sequence; } - } - }*/ - } - - protected void ResendUnacked() - { - int now = System.Environment.TickCount; - - lock (m_needAck) - { - foreach (Packet packet in m_needAck.Values) - { - if ((now - packet.TickCount > RESEND_TIMEOUT) && (!packet.Header.Resent)) - { - //m_log.Debug("[NETWORK]: Resending " + packet.Type.ToString() + " packet, " + - //(now - packet.TickCount) + "ms have passed"); - - packet.Header.Resent = true; - OutPacket(packet, ThrottleOutPacketType.Resend); - } - } - } - } - - protected void SendAcks() - { - lock (m_pendingAcks) - { - if (m_pendingAcks.Count > 0) - { - if (m_pendingAcks.Count > 250) - { - // FIXME: Handle the odd case where we have too many pending ACKs queued up - m_log.Info("[NETWORK]: Too many ACKs queued up!"); - return; - } - - //m_log.Info("[NETWORK]: Sending PacketAck"); - - int i = 0; - PacketAckPacket acks = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck); - // TODO: don't create new blocks if recycling an old packet - acks.Packets = new PacketAckPacket.PacketsBlock[m_pendingAcks.Count]; - - foreach (uint ack in m_pendingAcks.Values) - { - acks.Packets[i] = new PacketAckPacket.PacketsBlock(); - acks.Packets[i].ID = ack; - i++; - } - - acks.Header.Reliable = false; - OutPacket(acks, ThrottleOutPacketType.Unknown); - - m_pendingAcks.Clear(); - } - } - } - - protected void AckTimer_Elapsed(object sender, ElapsedEventArgs ea) - { - SendAcks(); - ResendUnacked(); - SendPacketStats(); - // TerrainPacketTrack(); - } - - /// - /// Keeps track of the packet stats for the simulator stats reporter - /// - protected void SendPacketStats() - { - handlerPacketStats = OnPacketStats; - if (handlerPacketStats != null) - { - handlerPacketStats(m_packetsReceived - m_lastPacketsReceivedSentToScene, m_packetsSent - m_lastPacketsSentSentToScene, m_unAckedBytes); - m_lastPacketsReceivedSentToScene = m_packetsReceived; - m_lastPacketsSentSentToScene = m_packetsSent; - } - } - - - /// - /// Emties out the old packets in the packet duplication tracking table. - /// - protected void ClearOldPacketDupeTracking() - { - lock (m_dupeLimiter) - { - List toEliminate = new List(); - try - { - foreach (uint seq in m_dupeLimiter.Keys) - { - PacketDupeLimiter pkdata = null; - m_dupeLimiter.TryGetValue(seq, out pkdata); - if (pkdata != null) - { - // doing a foreach loop, so we don't want to modify the dictionary while we're searching it - if (Util.UnixTimeSinceEpoch() - pkdata.timeIn > m_clearDuplicatePacketTrackingOlderThenXSeconds) - toEliminate.Add(seq); - } - } - } - catch (InvalidOperationException) - { - m_log.Info("[PACKET]: Unable to clear dupe check packet data"); - } - - // remove the dupe packets that we detected in the loop above. - uint[] seqsToRemove = toEliminate.ToArray(); - for (int i = 0; i < seqsToRemove.Length; i++) - { - if (m_dupeLimiter.ContainsKey(seqsToRemove[i])) - m_dupeLimiter.Remove(seqsToRemove[i]); - } - } - } - - #endregion - - public void TriggerTerrainUnackedEvent(int patchX, int patchY) - { - handlerUnackedTerrain = OnUnackedTerrain; - if (handlerUnackedTerrain != null) - { - handlerUnackedTerrain(this, patchX, patchY); - } - } - - // Previously ClientView.ProcessPackets - public bool AddMoney(int debit) { if (m_moneyBalance + debit >= 0) @@ -4143,14 +3747,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - private bool m_packetProcessingEnabled = true; - - public bool IsActive - { - get { return m_packetProcessingEnabled; } - set { m_packetProcessingEnabled = value; } - } - /// /// Breaks down the genericMessagePacket into specific events /// @@ -4206,31 +3802,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// all UDP packets from the client will end up here /// /// libsecondlife.packet - protected void ProcessInPacket(Packet Pack) + public void ProcessInPacket(Packet Pack) { - // always ack the packet! - ack_pack(Pack); - - // check for duplicate packets.. packets that the client is - // resending because it didn't receive our ack - - lock (m_dupeLimiter) - { - if (m_dupeLimiter.ContainsKey(Pack.Header.Sequence)) - { - //m_log.Info("[CLIENT]: Warning Duplicate packet detected" + Pack.Type.ToString() + " Dropping."); - return; - } - else - { - PacketDupeLimiter pkdedupe = new PacketDupeLimiter(); - pkdedupe.packetId = Pack.Header.ID; - pkdedupe.pktype = Pack.Type; - pkdedupe.timeIn = Util.UnixTimeSinceEpoch(); - m_dupeLimiter.Add(Pack.Header.Sequence, pkdedupe); - } - } - // check if we've got a local packet handler for this packet.type. See RegisterLocalPacketHandlers() if (ProcessPacketMethod(Pack)) { @@ -4702,7 +4275,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP case PacketType.AgentThrottle: AgentThrottlePacket atpack = (AgentThrottlePacket)Pack; - m_packetQueue.SetThrottleFromClient(atpack.Throttle.Throttles); + m_PacketHandler.PacketQueue.SetThrottleFromClient(atpack.Throttle.Throttles); break; case PacketType.AgentPause: @@ -6648,76 +6221,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP public ClientInfo GetClientInfo() { - //MainLog.Instance.Verbose("CLIENT", "GetClientInfo BGN"); + ClientInfo info = m_PacketHandler.GetClientInfo(); - ClientInfo info = new ClientInfo(); info.userEP = this.m_userEndPoint; info.proxyEP = this.m_proxyEndPoint; info.agentcircuit = new sAgentCircuitData(RequestClientInfo()); - info.pendingAcks = m_pendingAcks; - - info.needAck = new Dictionary(); - - lock (m_needAck) - { - foreach (uint key in m_needAck.Keys) - { - info.needAck.Add(key, m_needAck[key].ToBytes()); - } - } - - /* pending - QueItem[] queitems = m_packetQueue.GetQueueArray(); - - MainLog.Instance.Verbose("CLIENT", "Queue Count : [{0}]", queitems.Length); - - for (int i = 0; i < queitems.Length; i++) - { - if (queitems[i].Incoming == false) - { - info.out_packets.Add(queitems[i].Packet.ToBytes()); - MainLog.Instance.Verbose("CLIENT", "Add OutPacket [{0}]", queitems[i].Packet.Type.ToString()); - } - } - */ - - info.sequence = m_sequence; - - //MainLog.Instance.Verbose("CLIENT", "GetClientInfo END"); - return info; } public void SetClientInfo(ClientInfo info) { - m_pendingAcks = info.pendingAcks; - - m_needAck = new Dictionary(); - - Packet packet = null; - int packetEnd = 0; - byte[] zero = new byte[3000]; - - foreach (uint key in info.needAck.Keys) - { - byte[] buff = info.needAck[key]; - - packetEnd = buff.Length - 1; - - try - { - packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero); - } - catch (Exception) - { - - } - - m_needAck.Add(key, packet); - } - - m_sequence = info.sequence; + m_PacketHandler.SetClientInfo(info); } #region Media Parcel Members diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs new file mode 100644 index 0000000000..1ec375fd9d --- /dev/null +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketHandler.cs @@ -0,0 +1,692 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Timers; +using libsecondlife; +using libsecondlife.Packets; +using Timer = System.Timers.Timer; +using OpenSim.Framework; + +namespace OpenSim.Region.ClientStack.LindenUDP +{ + public delegate void PacketStats(int inPackets, int outPackets, int unAckedBytes); + public delegate void PacketDrop(Packet pack, Object id); + public delegate bool SynchronizeClientHandler(IScene scene, Packet packet, LLUUID agentID, ThrottleOutPacketType throttlePacketType); + + public interface IPacketHandler + { + event PacketStats OnPacketStats; + event PacketDrop OnPacketDrop; + SynchronizeClientHandler SynchronizeClient { set; } + + int PacketsReceived { get; } + int PacketsReceivedReported { get; } + uint SilenceLimit { get; set; } + uint DiscardTimeout { get; set; } + uint ResendTimeout { get; set; } + + void InPacket(Packet packet); + void ProcessInPacket(Packet packet); + void OutPacket(Packet NewPack, + ThrottleOutPacketType throttlePacketType); + void OutPacket(Packet NewPack, + ThrottleOutPacketType throttlePacketType, Object id); + LLPacketQueue PacketQueue { get; } + void Stop(); + void Flush(); + void Clear(); + ClientInfo GetClientInfo(); + void SetClientInfo(ClientInfo info); + void AddImportantPacket(PacketType type); + void RemoveImportantPacket(PacketType type); + } + + public class LLPacketHandler : IPacketHandler + { + // Packet queues + // + LLPacketQueue m_PacketQueue; + + public LLPacketQueue PacketQueue + { + get { return m_PacketQueue; } + } + + // Timer to run stats and acks on + // + private Timer m_AckTimer = new Timer(250); + + // A list of the packets we haven't acked yet + // + private Dictionary m_PendingAcks = new Dictionary(); + // Dictionary of the packets that need acks from the client. + // + private class AckData + { + public AckData(Packet packet, Object identifier) + { + Packet = packet; + Identifier = identifier; + } + + public Packet Packet; + public Object Identifier; + } + private Dictionary m_NeedAck = + new Dictionary(); + + private uint m_ResendTimeout = 1000; + + public uint ResendTimeout + { + get { return m_ResendTimeout; } + set { m_ResendTimeout = value; } + } + + private uint m_DiscardTimeout = 8000; + + public uint DiscardTimeout + { + get { return m_DiscardTimeout; } + set { m_DiscardTimeout = value; } + } + + private uint m_SilenceLimit = 250; + + public uint SilenceLimit + { + get { return m_SilenceLimit; } + set { m_SilenceLimit = value; } + } + + private int m_LastAck = 0; + + // Track duplicated packets. This uses a Dictionary. Both insertion + // and lookup are common operations and need to take advantage of + // the hashing. Expiration is less common and can be allowed the + // time for a linear scan. + // + private Dictionary m_DupeTracker = + new Dictionary(); + private uint m_DupeTrackerWindow = 30; + + // Values for the SimStatsReporter + // + private int m_PacketsReceived = 0; + private int m_PacketsReceivedReported = 0; + private int m_PacketsSent = 0; + private int m_PacketsSentReported = 0; + private int m_UnackedBytes = 0; + + public int PacketsReceived + { + get { return m_PacketsReceived; } + } + + public int PacketsReceivedReported + { + get { return m_PacketsReceivedReported; } + } + + // The client we are working for + // + private IClientAPI m_Client; + + // Some events + // + public event PacketStats OnPacketStats; + public event PacketDrop OnPacketDrop; + + private SynchronizeClientHandler m_SynchronizeClient = null; + + public SynchronizeClientHandler SynchronizeClient + { + set { m_SynchronizeClient = value; } + } + + // Packet sequencing + // + private uint m_Sequence = 0; + private object m_SequenceLock = new object(); + private const int MAX_SEQUENCE = 0xFFFFFF; + + List m_ImportantPackets = new List(); + + //////////////////////////////////////////////////////////////////// + + // Constructors + // + public LLPacketHandler(IClientAPI client) + { + m_Client = client; + + m_PacketQueue = new LLPacketQueue(client.AgentId); + + m_AckTimer.Elapsed += AckTimerElapsed; + m_AckTimer.Start(); + } + + public void Stop() + { + m_AckTimer.Stop(); + + m_PacketQueue.Enqueue(null); + } + + // Send one packet. This actually doesn't send anything, it queues + // it. Designed to be fire-and-forget, but there is an optional + // notifier. + // + public void OutPacket( + Packet packet, ThrottleOutPacketType throttlePacketType) + { + OutPacket(packet, throttlePacketType, null); + } + + public void OutPacket( + Packet packet, ThrottleOutPacketType throttlePacketType, + Object id) + { + // Call the load balancer's hook. If this is not active here + // we defer to the sim server this client is actually connected + // to. Packet drop notifies will not be triggered in this + // configuration! + // + if ((m_SynchronizeClient != null) && (!m_Client.IsActive)) + { + if (m_SynchronizeClient(m_Client.Scene, packet, + m_Client.AgentId, throttlePacketType)) + return; + } + + packet.Header.Sequence = NextPacketSequenceNumber(); + + lock(m_NeedAck) + { + DropResend(id); + + QueuePacket(packet, throttlePacketType, id); + + // We want to see that packet arrive if it's reliable + if(packet.Header.Reliable) + { + m_UnackedBytes += packet.ToBytes().Length; + m_NeedAck[packet.Header.Sequence] = new AckData(packet, id); + } + } + } + + private void QueuePacket( + Packet packet, ThrottleOutPacketType throttlePacketType, + Object id) + { + // Add acks to outgoing packets + // + lock(m_PendingAcks) + { + if(m_PendingAcks.Count > 0) + { + int count = m_PendingAcks.Count; + if(count > 10) + count = 10; + packet.Header.AckList = new uint[count]; + + int i = 0; + + foreach (uint ack in new List(m_PendingAcks.Keys)) + { + packet.Header.AckList[i] = ack; + i++; + m_PendingAcks.Remove(ack); + if (i >= 10) // That is how much space there is + break; + } + } + } + + packet.TickCount = System.Environment.TickCount; + + LLQueItem item = new LLQueItem(); + item.Packet = packet; + item.Incoming = false; + item.throttleType = throttlePacketType; + item.Identifier = id; + + m_PacketQueue.Enqueue(item); + m_PacketsSent++; + } + + private void ResendUnacked() + { + int now = System.Environment.TickCount; + int lastAck = m_LastAck; + + // Unless we have received at least one ack, don't bother resending + // anything. There may not be a client there, don't clog up the + // pipes. + // + if(lastAck == 0) + return; + + lock (m_NeedAck) + { + // Nothing to do + // + if(m_NeedAck.Count == 0) + return; + + // If we have seen no acks in s but are + // waiting for acks, then there may be no one listening. + // No need to resend anything. Keep it until it gets stale, + // then it will be dropped. + // + if (((now - lastAck) > m_SilenceLimit) && + m_NeedAck.Count > 0) + { + return; + } + + foreach (AckData data in new List(m_NeedAck.Values)) + { + Packet packet = data.Packet; + + // Packets this old get resent + // + if ((now - packet.TickCount) > m_ResendTimeout) + { + // Resend the packet. Set the packet's tick count to + // now, and keep it marked as resent. + // + packet.Header.Resent = true; + QueuePacket(packet, ThrottleOutPacketType.Resend, + data.Identifier); + } + + // The discard logic + // If the packet is in the queue for s + // without having been processed, then we have clogged + // pipes. Most likely, the client is gone + // Drop the packets + // + if ((now - packet.TickCount) > m_DiscardTimeout) + { + if(!m_ImportantPackets.Contains(packet.Type)) + m_NeedAck.Remove(packet.Header.Sequence); + + TriggerOnPacketDrop(packet, data.Identifier); + + continue; + } + } + } + } + + // Send the pending packet acks to the client + // Will send blocks of acks for up to 250 packets + // + private void SendAcks() + { + lock (m_PendingAcks) + { + if (m_PendingAcks.Count == 0) + return; + + PacketAckPacket acks = (PacketAckPacket)PacketPool.Instance.GetPacket(PacketType.PacketAck); + + // The case of equality is more common than one might think, + // because this function will be called unconditionally when + // the counter reaches 250. So there is a good chance another + // packet with 250 blocks exists. + // + if(acks.Packets == null || + acks.Packets.Length != m_PendingAcks.Count) + acks.Packets = new PacketAckPacket.PacketsBlock[m_PendingAcks.Count]; + int i = 0; + foreach (uint ack in new List(m_PendingAcks.Keys)) + { + acks.Packets[i] = new PacketAckPacket.PacketsBlock(); + acks.Packets[i].ID = ack; + + m_PendingAcks.Remove(ack); + i++; + } + + acks.Header.Reliable = false; + OutPacket(acks, ThrottleOutPacketType.Unknown); + } + } + + // Queue a packet ack. It will be sent either after 250 acks are + // queued, or when the timer fires. + // + private void AckPacket(Packet packet) + { + lock (m_PendingAcks) + { + if(m_PendingAcks.Count < 250) + { + if(!m_PendingAcks.ContainsKey(packet.Header.Sequence)) + m_PendingAcks.Add(packet.Header.Sequence, + packet.Header.Sequence); + return; + } + } + + SendAcks(); + + lock (m_PendingAcks) + { + // If this is still full we have a truly exceptional + // condition (means, can't happen) + // + if(m_PendingAcks.Count < 250) + { + if(!m_PendingAcks.ContainsKey(packet.Header.Sequence)) + m_PendingAcks.Add(packet.Header.Sequence, + packet.Header.Sequence); + return; + } + } + } + + // When the timer elapses, send the pending acks, trigger resends + // and report all the stats. + // + private void AckTimerElapsed(object sender, ElapsedEventArgs ea) + { + SendAcks(); + ResendUnacked(); + SendPacketStats(); + } + + // Push out pachet counts for the sim status reporter + // + private void SendPacketStats() + { + PacketStats handlerPacketStats = OnPacketStats; + if (handlerPacketStats != null) + { + handlerPacketStats( + m_PacketsReceived - m_PacketsReceivedReported, + m_PacketsSent - m_PacketsSentReported, + m_UnackedBytes); + + m_PacketsReceivedReported = m_PacketsReceived; + m_PacketsSentReported = m_PacketsSent; + } + } + + // We can't keep an unlimited record of dupes. This will prune the + // dictionary by age. + // + private void PruneDupeTracker() + { + lock (m_DupeTracker) + { + Dictionary packs = + new Dictionary(m_DupeTracker); + + foreach (uint pack in packs.Keys) + { + if(Util.UnixTimeSinceEpoch() - m_DupeTracker[pack] > + m_DupeTrackerWindow) + m_DupeTracker.Remove(pack); + } + } + } + + public void InPacket(Packet packet) + { + if(packet == null) + return; + + // If this client is on another partial instance, no need + // to handle packets + // + if(!m_Client.IsActive && packet.Type != PacketType.LogoutRequest) + { + PacketPool.Instance.ReturnPacket(packet); + return; + } + + // Any packet can have some packet acks in the header. + // Process them here + // + if(packet.Header.AppendedAcks) + { + foreach(uint id in packet.Header.AckList) + { + ProcessAck(id); + } + } + + // When too many acks are needed to be sent, the client sends + // a packet consisting of acks only + // + if(packet.Type == PacketType.PacketAck) + { + PacketAckPacket ackPacket = (PacketAckPacket)packet; + + foreach (PacketAckPacket.PacketsBlock block in + ackPacket.Packets) + { + ProcessAck(block.ID); + } + + PacketPool.Instance.ReturnPacket(packet); + return; + } + else if(packet.Type == PacketType.StartPingCheck) + { + StartPingCheckPacket startPing = (StartPingCheckPacket)packet; + CompletePingCheckPacket endPing = (CompletePingCheckPacket)PacketPool.Instance.GetPacket(PacketType.CompletePingCheck); + + endPing.PingID.PingID = startPing.PingID.PingID; + OutPacket(endPing, ThrottleOutPacketType.Task); + } + else + { + LLQueItem item = new LLQueItem(); + item.Packet = packet; + item.Incoming = true; + m_PacketQueue.Enqueue(item); + } + } + + public void ProcessInPacket(Packet packet) + { + // Always ack the packet! + // + AckPacket(packet); + + if (packet.Type != PacketType.AgentUpdate) + m_PacketsReceived++; + + // Check for duplicate packets.. packets that the client is + // resending because it didn't receive our ack + // + lock (m_DupeTracker) + { + if (m_DupeTracker.ContainsKey(packet.Header.Sequence)) + return; + + m_DupeTracker.Add(packet.Header.Sequence, + Util.UnixTimeSinceEpoch()); + } + + m_Client.ProcessInPacket(packet); + } + + public void Flush() + { + m_PacketQueue.Flush(); + } + + public void Clear() + { + m_NeedAck.Clear(); + m_PendingAcks.Clear(); + m_Sequence += 1000000; + } + + private void ProcessAck(uint id) + { + AckData data; + Packet packet; + + lock(m_NeedAck) + { + if(!m_NeedAck.TryGetValue(id, out data)) + return; + + packet = data.Packet; + + m_NeedAck.Remove(id); + m_UnackedBytes -= packet.ToBytes().Length; + + m_LastAck = System.Environment.TickCount; + } + } + + // Allocate packet sequence numbers in a threadsave manner + // + protected uint NextPacketSequenceNumber() + { + // Set the sequence number + uint seq = 1; + lock (m_SequenceLock) + { + if (m_Sequence >= MAX_SEQUENCE) + { + m_Sequence = 1; + } + else + { + m_Sequence++; + } + seq = m_Sequence; + } + return seq; + } + + public ClientInfo GetClientInfo() + { + ClientInfo info = new ClientInfo(); + info.pendingAcks = m_PendingAcks; + info.needAck = new Dictionary(); + + lock (m_NeedAck) + { + foreach (uint key in m_NeedAck.Keys) + info.needAck.Add(key, m_NeedAck[key].Packet.ToBytes()); + } + + LLQueItem[] queitems = m_PacketQueue.GetQueueArray(); + + for (int i = 0; i < queitems.Length; i++) + { + if (queitems[i].Incoming == false) + info.out_packets.Add(queitems[i].Packet.ToBytes()); + } + + info.sequence = m_Sequence; + + return info; + } + + public void SetClientInfo(ClientInfo info) + { + m_PendingAcks = info.pendingAcks; + m_NeedAck = new Dictionary(); + + Packet packet = null; + int packetEnd = 0; + byte[] zero = new byte[3000]; + + foreach (uint key in info.needAck.Keys) + { + byte[] buff = info.needAck[key]; + packetEnd = buff.Length - 1; + + try + { + packet = PacketPool.Instance.GetPacket(buff, ref packetEnd, zero); + } + catch (Exception) + { + } + + m_NeedAck.Add(key, new AckData(packet, null)); + } + + m_Sequence = info.sequence; + } + + public void AddImportantPacket(PacketType type) + { + if(m_ImportantPackets.Contains(type)) + return; + + m_ImportantPackets.Add(type); + } + + public void RemoveImportantPacket(PacketType type) + { + if(!m_ImportantPackets.Contains(type)) + return; + + m_ImportantPackets.Remove(type); + } + + private void DropResend(Object id) + { + foreach (AckData data in new List(m_NeedAck.Values)) + { + if(data.Identifier != null && data.Identifier == id) + { + m_NeedAck.Remove(data.Packet.Header.Sequence); + return; + } + } + } + + private void TriggerOnPacketDrop(Packet packet, Object id) + { + PacketDrop handlerPacketDrop = OnPacketDrop; + + if(handlerPacketDrop == null) + return; + + handlerPacketDrop(packet, id); + } + } +} diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs index 51dcde7d01..96f8ac6679 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLPacketQueue.cs @@ -54,6 +54,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP private Queue WindOutgoingPacketQueue; private Queue CloudOutgoingPacketQueue; private Queue TaskOutgoingPacketQueue; + private Queue TaskLowpriorityPacketQueue; private Queue TextureOutgoingPacketQueue; private Queue AssetOutgoingPacketQueue; @@ -99,6 +100,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP WindOutgoingPacketQueue = new Queue(); CloudOutgoingPacketQueue = new Queue(); TaskOutgoingPacketQueue = new Queue(); + TaskLowpriorityPacketQueue = new Queue(); TextureOutgoingPacketQueue = new Queue(); AssetOutgoingPacketQueue = new Queue(); @@ -106,8 +108,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Set up the throttle classes (min, max, current) in bytes ResendThrottle = new LLPacketThrottle(5000, 100000, 16000); LandThrottle = new LLPacketThrottle(1000, 100000, 2000); - WindThrottle = new LLPacketThrottle(1000, 100000, 1000); - CloudThrottle = new LLPacketThrottle(1000, 100000, 1000); + WindThrottle = new LLPacketThrottle(0, 100000, 0); + CloudThrottle = new LLPacketThrottle(0, 100000, 0); TaskThrottle = new LLPacketThrottle(1000, 800000, 3000); AssetThrottle = new LLPacketThrottle(1000, 800000, 1000); TextureThrottle = new LLPacketThrottle(1000, 800000, 4000); @@ -149,6 +151,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; } + if (item.Incoming) + { + SendQueue.PriorityEnqueue(item); + return; + } + lock (this) { switch (item.throttleType) @@ -162,6 +170,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP case ThrottleOutPacketType.Task: ThrottleCheck(ref TaskThrottle, ref TaskOutgoingPacketQueue, item); break; + case ThrottleOutPacketType.LowpriorityTask: + ThrottleCheck(ref TaskThrottle, ref TaskLowpriorityPacketQueue, item); + break; case ThrottleOutPacketType.Land: ThrottleCheck(ref LandThrottle, ref LandOutgoingPacketQueue, item); break; @@ -178,7 +189,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP default: // Acknowledgements and other such stuff should go directly to the blocking Queue // Throttling them may and likely 'will' be problematic - SendQueue.Enqueue(item); + SendQueue.PriorityEnqueue(item); break; } } @@ -214,7 +225,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP } if (TaskOutgoingPacketQueue.Count > 0) { - SendQueue.Enqueue(TaskOutgoingPacketQueue.Dequeue()); + SendQueue.PriorityEnqueue(TaskOutgoingPacketQueue.Dequeue()); + } + if (TaskLowpriorityPacketQueue.Count > 0) + { + SendQueue.Enqueue(TaskLowpriorityPacketQueue.Dequeue()); } if (TextureOutgoingPacketQueue.Count > 0) { @@ -261,6 +276,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP WindOutgoingPacketQueue.Count > 0 || CloudOutgoingPacketQueue.Count > 0 || TaskOutgoingPacketQueue.Count > 0 || + TaskLowpriorityPacketQueue.Count > 0 || AssetOutgoingPacketQueue.Count > 0 || TextureOutgoingPacketQueue.Count > 0); } @@ -319,11 +335,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP TotalThrottle.Add(qpack.Packet.ToBytes().Length); CloudThrottle.Add(qpack.Packet.ToBytes().Length); } - if (TaskThrottle.UnderLimit() && TaskOutgoingPacketQueue.Count > 0) + if (TaskThrottle.UnderLimit() && (TaskOutgoingPacketQueue.Count > 0 || TaskLowpriorityPacketQueue.Count > 0)) { - LLQueItem qpack = TaskOutgoingPacketQueue.Dequeue(); - - SendQueue.Enqueue(qpack); + LLQueItem qpack; + if(TaskOutgoingPacketQueue.Count > 0) + { + qpack = TaskOutgoingPacketQueue.Dequeue(); + SendQueue.PriorityEnqueue(qpack); + } + else + { + qpack = TaskLowpriorityPacketQueue.Dequeue(); + SendQueue.Enqueue(qpack); + } TotalThrottle.Add(qpack.Packet.ToBytes().Length); TaskThrottle.Add(qpack.Packet.ToBytes().Length); } @@ -490,18 +514,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP AssetThrottle.Throttle = tAsset; TotalThrottle.Throttle = tall; } - else if (tall < 1) - { - // client is stupid, penalize him by minning everything - ResendThrottle.Throttle = ResendThrottle.Min; - LandThrottle.Throttle = LandThrottle.Min; - WindThrottle.Throttle = WindThrottle.Min; - CloudThrottle.Throttle = CloudThrottle.Min; - TaskThrottle.Throttle = TaskThrottle.Min; - TextureThrottle.Throttle = TextureThrottle.Min; - AssetThrottle.Throttle = AssetThrottle.Min; - TotalThrottle.Throttle = TotalThrottle.Min; - } +// else if (tall < 1) +// { +// // client is stupid, penalize him by minning everything +// ResendThrottle.Throttle = ResendThrottle.Min; +// LandThrottle.Throttle = LandThrottle.Min; +// WindThrottle.Throttle = WindThrottle.Min; +// CloudThrottle.Throttle = CloudThrottle.Min; +// TaskThrottle.Throttle = TaskThrottle.Min; +// TextureThrottle.Throttle = TextureThrottle.Min; +// AssetThrottle.Throttle = AssetThrottle.Min; +// TotalThrottle.Throttle = TotalThrottle.Min; +// } else { // we're over so figure out percentages and use those @@ -516,7 +540,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP TotalThrottle.Throttle = TotalThrottle.Max; } // effectively wiggling the slider causes things reset - ResetCounters(); +// ResetCounters(); // DO NOT reset, better to send less for one period than more } // See IPullStatsProvider @@ -540,4 +564,4 @@ namespace OpenSim.Region.ClientStack.LindenUDP return SendQueue.GetQueueArray(); } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLPacketTracker.cs b/OpenSim/Region/ClientStack/LindenUDP/LLPacketTracker.cs deleted file mode 100644 index 690fadb95f..0000000000 --- a/OpenSim/Region/ClientStack/LindenUDP/LLPacketTracker.cs +++ /dev/null @@ -1,263 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using libsecondlife; -using libsecondlife.Packets; -using OpenSim.Framework; - -namespace OpenSim.Region.ClientStack.LindenUDP -{ - - public class LLPacketTracker - { - public delegate void PacketAcked(uint sequenceNumber); - public event PacketAcked OnPacketAcked; - - protected List m_beenAcked = new List(); - - protected TerrainPacketTracker[,] m_sentTerrainPackets = new TerrainPacketTracker[16, 16]; - protected Dictionary m_sendPrimPackets = new Dictionary(); - - protected LLClientView m_parentClient; - - public LLPacketTracker(LLClientView parent) - { - m_parentClient = parent; - OnPacketAcked += TerrainPacketAcked; - //OnPacketAcked += PrimPacketAcked; - } - - public void PacketAck(uint sequenceNumber) - { - lock (m_beenAcked) - { - m_beenAcked.Add(sequenceNumber); - } - } - - public void TrackTerrainPacket(uint sequenceNumber, int patchX, int patchY) - { - TrackTerrainPacket(sequenceNumber, patchX, patchY, false, null); - } - - public void TrackTerrainPacket(uint sequenceNumber, int patchX, int patchY, bool keepResending, LayerDataPacket packet) - { - TerrainPacketTracker tracker = new TerrainPacketTracker(); - tracker.X = patchX; - tracker.Y = patchY; - tracker.SeqNumber = sequenceNumber; - tracker.TimeSent = DateTime.Now; - tracker.KeepResending = keepResending; - tracker.Packet = packet; - lock (m_sentTerrainPackets) - { - m_sentTerrainPackets[patchX, patchY] = tracker; - } - } - - public void TrackPrimPacket(uint sequenceNumber, LLUUID primID) - { - PrimPacketTracker tracker = new PrimPacketTracker(); - tracker.PrimID = primID; - tracker.TimeSent = DateTime.Now; - tracker.SeqNumber = sequenceNumber; - lock (m_sendPrimPackets) - { - m_sendPrimPackets[primID] = tracker; - } - } - - public void TerrainPacketCheck() - { - DateTime now = DateTime.Now; - List resendList = new List(); - lock (m_sentTerrainPackets) - { - for (int y = 0; y < 16; y++) - { - for (int x = 0; x < 16; x++) - { - if (m_sentTerrainPackets[x, y] != null) - { - TerrainPacketTracker tracker = m_sentTerrainPackets[x, y]; - if ((now - tracker.TimeSent) > TimeSpan.FromMinutes(1)) - { - tracker.TimeSent = now; - m_sentTerrainPackets[x, y] = null; - resendList.Add(tracker); - } - } - } - } - } - - foreach (TerrainPacketTracker tracker in resendList) - { - if (!tracker.KeepResending) - { - m_parentClient.TriggerTerrainUnackedEvent(tracker.X, tracker.Y); - } - else - { - if (tracker.Packet != null) - { - tracker.Packet.Header.Resent = true; - m_parentClient.OutPacket(tracker.Packet, ThrottleOutPacketType.Resend); - tracker.TimeSent = DateTime.Now; - lock (m_sentTerrainPackets) - { - if (m_sentTerrainPackets[tracker.X, tracker.Y] == null) - { - m_sentTerrainPackets[tracker.X, tracker.Y] = tracker; - } - } - } - } - } - } - - public void PrimPacketCheck() - { - DateTime now = DateTime.Now; - List resendList = new List(); - List ackedList = new List(); - - lock (m_sendPrimPackets) - { - foreach (PrimPacketTracker tracker in m_sendPrimPackets.Values) - { - if (tracker.Acked) - { - ackedList.Add(tracker); - } - else if (((now - tracker.TimeSent) > TimeSpan.FromMinutes(1)) && (!tracker.Acked)) - { - resendList.Add(tracker); - } - } - } - - foreach (PrimPacketTracker tracker in resendList) - { - lock (m_sendPrimPackets) - { - m_sendPrimPackets.Remove(tracker.PrimID); - } - //call event - Console.WriteLine("Prim packet not acked, " + tracker.PrimID.ToString()); - } - - - RemovePrimTrackers(ackedList); - } - - public void PrimTrackerCleanup() - { - List ackedList = new List(); - - lock (m_sendPrimPackets) - { - foreach (PrimPacketTracker tracker in m_sendPrimPackets.Values) - { - if (tracker.Acked) - { - ackedList.Add(tracker); - } - } - } - Thread.Sleep(15); //give a little bit of time for other code to access list before we lock it again - - RemovePrimTrackers(ackedList); - } - - protected void RemovePrimTrackers(List ackedList) - { - lock (m_sendPrimPackets) - { - foreach (PrimPacketTracker tracker in ackedList) - { - m_sendPrimPackets.Remove(tracker.PrimID); - } - } - } - - protected void TerrainPacketAcked(uint sequence) - { - lock (m_sentTerrainPackets) - { - for (int y = 0; y < 16; y++) - { - for (int x = 0; x < 16; x++) - { - if (m_sentTerrainPackets[x, y] != null) - { - if (m_sentTerrainPackets[x, y].SeqNumber == sequence) - { - m_sentTerrainPackets[x, y] = null; - return; - } - } - } - } - } - } - - protected void PrimPacketAcked(uint sequence) - { - lock (m_sendPrimPackets) - { - foreach (PrimPacketTracker tracker in m_sendPrimPackets.Values) - { - if (tracker.SeqNumber == sequence) - { - tracker.Acked = true; - break; - } - } - } - } - - public void Process() - { - List ackedPackets = null; - lock (m_beenAcked) - { - ackedPackets = new List(m_beenAcked); - m_beenAcked.Clear(); - } - - if (ackedPackets != null) - { - foreach (uint packetId in ackedPackets) - { - if (OnPacketAcked != null) - { - OnPacketAcked(packetId); - } - } - } - - // ackedPackets.Clear(); - ackedPackets = null; - } - - public class TerrainPacketTracker - { - public uint SeqNumber = 0; - public int X; - public int Y; - public DateTime TimeSent; - public LayerDataPacket Packet; - public bool KeepResending; - } - - public class PrimPacketTracker - { - public uint SeqNumber = 0; - public DateTime TimeSent; - public LLUUID PrimID; - public bool Acked = false; - } - } -} diff --git a/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs b/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs index 2b6e78155f..e836dd7d9f 100644 --- a/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs +++ b/OpenSim/Region/ClientStack/LindenUDP/LLQueItem.cs @@ -25,6 +25,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +using System; using libsecondlife.Packets; using OpenSim.Framework; @@ -39,5 +40,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP public Packet Packet; public bool Incoming; public ThrottleOutPacketType throttleType; + public Object Identifier; } -} \ No newline at end of file +} diff --git a/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs b/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs index 574bc63f2d..72520c45d5 100644 --- a/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/Environment/Modules/World/NPC/NPCAvatar.cs @@ -51,6 +51,11 @@ namespace OpenSim.Region.Environment.Modules.World.NPC m_scene = scene; } + public IScene Scene + { + get { return m_scene; } + } + public void Say(string message) { SendOnChatFromViewer(message, ChatTypeEnum.Say); @@ -259,7 +264,6 @@ namespace OpenSim.Region.Environment.Modules.World.NPC public event FriendActionDelegate OnApproveFriendRequest; public event FriendActionDelegate OnDenyFriendRequest; public event FriendshipTermination OnTerminateFriendship; - public event PacketStats OnPacketStats; public event EconomyDataRequest OnEconomyDataRequest; public event MoneyBalanceRequest OnMoneyBalanceRequest; @@ -518,7 +522,7 @@ namespace OpenSim.Region.Environment.Modules.World.NPC LLVector3 acc, LLQuaternion rotation, LLVector3 rvel, uint flags, LLUUID objectID, LLUUID ownerID, string text, byte[] color, uint parentID, - byte[] particleSystem, byte clickAction, bool track) + byte[] particleSystem, byte clickAction) { } public virtual void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, @@ -527,7 +531,7 @@ namespace OpenSim.Region.Environment.Modules.World.NPC LLUUID objectID, LLUUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, byte clickAction, byte[] textureanimation, - bool attachment, uint AttachmentPoint, LLUUID AssetId, LLUUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius, bool track) + bool attachment, uint AttachmentPoint, LLUUID AssetId, LLUUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius) { } public virtual void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, @@ -711,6 +715,10 @@ namespace OpenSim.Region.Environment.Modules.World.NPC { } + public void ProcessInPacket(Packet NewPack) + { + } + public void Close(bool ShutdownCircuit) { } diff --git a/OpenSim/Region/Environment/Scenes/Scene.cs b/OpenSim/Region/Environment/Scenes/Scene.cs index 626aaaaa45..6bf552c240 100644 --- a/OpenSim/Region/Environment/Scenes/Scene.cs +++ b/OpenSim/Region/Environment/Scenes/Scene.cs @@ -2088,7 +2088,6 @@ namespace OpenSim.Region.Environment.Scenes client.OnMoneyTransferRequest += ProcessMoneyTransferRequest; client.OnParcelBuy += ProcessParcelBuy; client.OnAvatarPickerRequest += ProcessAvatarPickerRequest; - client.OnPacketStats += AddPacketStats; client.OnObjectIncludeInSearch += m_innerScene.MakeObjectSearchable; client.OnTeleportHomeRequest += TeleportClientHome; client.OnSetStartLocationRequest += SetHomeRezPoint; diff --git a/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs b/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs index 253a83efad..156310b9e9 100644 --- a/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs @@ -2085,7 +2085,7 @@ namespace OpenSim.Region.Environment.Scenes remoteClient.SendPrimitiveToClient(m_regionHandle, (ushort)(m_parentGroup.GetTimeDilation() * (float)ushort.MaxValue), LocalId, m_shape, lPos, Velocity, Acceleration, RotationOffset, RotationalVelocity, clientFlags, m_uuid, _ownerID, m_text, color, _parentID, m_particleSystem, m_clickAction, m_TextureAnimation, m_IsAttachment, - m_attachmentPoint,fromAssetID, Sound, SoundGain, SoundFlags, SoundRadius, false); + m_attachmentPoint,fromAssetID, Sound, SoundGain, SoundFlags, SoundRadius); } /// diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index c975df5ee6..bc675ff47a 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -42,6 +42,7 @@ namespace OpenSim.Region.Examples.SimpleModule private LLQuaternion bodyDirection = LLQuaternion.Identity; private short count = 0; private short frame = 0; + private Scene m_scene; // disable warning: public events, part of the public API #pragma warning disable 67 @@ -165,7 +166,6 @@ namespace OpenSim.Region.Examples.SimpleModule public event FriendActionDelegate OnApproveFriendRequest; public event FriendActionDelegate OnDenyFriendRequest; public event FriendshipTermination OnTerminateFriendship; - public event PacketStats OnPacketStats; public event EconomyDataRequest OnEconomyDataRequest; public event MoneyBalanceRequest OnMoneyBalanceRequest; @@ -208,10 +208,12 @@ namespace OpenSim.Region.Examples.SimpleModule private LLUUID myID = LLUUID.Random(); - public MyNpcCharacter(EventManager eventManager) + public MyNpcCharacter(Scene scene) { + // startPos = new LLVector3(128, (float)(Util.RandomClass.NextDouble()*100), 2); - eventManager.OnFrame += Update; + m_scene = scene; + m_scene.EventManager.OnFrame += Update; } private LLVector3 startPos = new LLVector3(128, 128, 2); @@ -280,6 +282,11 @@ namespace OpenSim.Region.Examples.SimpleModule get { return 1; } } + public IScene Scene + { + get { return m_scene; } + } + public virtual void OutPacket(Packet newPack, ThrottleOutPacketType packType) { } @@ -432,7 +439,7 @@ namespace OpenSim.Region.Examples.SimpleModule LLVector3 acc, LLQuaternion rotation, LLVector3 rvel, uint flags, LLUUID objectID, LLUUID ownerID, string text, byte[] color, uint parentID, - byte[] particleSystem, byte clickAction, bool track) + byte[] particleSystem, byte clickAction) { } public virtual void SendPrimitiveToClient(ulong regionHandle, ushort timeDilation, uint localID, @@ -441,7 +448,7 @@ namespace OpenSim.Region.Examples.SimpleModule LLUUID objectID, LLUUID ownerID, string text, byte[] color, uint parentID, byte[] particleSystem, byte clickAction, byte[] textureanimation, - bool attachment, uint AttachmentPoint, LLUUID AssetId, LLUUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius, bool track) + bool attachment, uint AttachmentPoint, LLUUID AssetId, LLUUID SoundId, double SoundVolume, byte SoundFlags, double SoundRadius) { } public virtual void SendPrimTerseUpdate(ulong regionHandle, ushort timeDilation, uint localID, @@ -700,6 +707,10 @@ namespace OpenSim.Region.Examples.SimpleModule { } + public void ProcessInPacket(Packet NewPack) + { + } + public void Close(bool ShutdownCircuit) { } diff --git a/OpenSim/Region/Examples/SimpleModule/RegionModule.cs b/OpenSim/Region/Examples/SimpleModule/RegionModule.cs index 46b7efdb7e..441c69697b 100644 --- a/OpenSim/Region/Examples/SimpleModule/RegionModule.cs +++ b/OpenSim/Region/Examples/SimpleModule/RegionModule.cs @@ -84,7 +84,7 @@ namespace OpenSim.Region.Examples.SimpleModule { for (int i = 0; i < 1; i++) { - MyNpcCharacter m_character = new MyNpcCharacter(m_scene.EventManager); + MyNpcCharacter m_character = new MyNpcCharacter(m_scene); m_scene.AddNewClient(m_character, false); }