From d8f232c8b05a7f1954737c5f43be9da04a9844dd Mon Sep 17 00:00:00 2001 From: MW Date: Tue, 12 Jun 2007 18:11:14 +0000 Subject: --- OpenSim/OpenSim.RegionServer/ClientViewBase.cs | 327 +++++++++++++++++++++++++ 1 file changed, 327 insertions(+) create mode 100644 OpenSim/OpenSim.RegionServer/ClientViewBase.cs (limited to 'OpenSim/OpenSim.RegionServer/ClientViewBase.cs') diff --git a/OpenSim/OpenSim.RegionServer/ClientViewBase.cs b/OpenSim/OpenSim.RegionServer/ClientViewBase.cs new file mode 100644 index 0000000..facb3fa --- /dev/null +++ b/OpenSim/OpenSim.RegionServer/ClientViewBase.cs @@ -0,0 +1,327 @@ + +/* +* Copyright (c) Contributors, http://www.openmetaverse.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 libsecondlife; +using libsecondlife.Packets; +using System.Net; +using System.Net.Sockets; +using System.IO; +using System.Threading; +using System.Timers; +using OpenSim.Framework.Utilities; +using OpenSim.Framework.Interfaces; + +namespace OpenSim +{ + public class ClientViewBase + { + protected BlockingQueue PacketQueue; + protected Dictionary PendingAcks = new Dictionary(); + protected Dictionary NeedAck = new Dictionary(); + + protected System.Timers.Timer AckTimer; + protected uint Sequence = 0; + protected object SequenceLock = new object(); + protected const int MAX_APPENDED_ACKS = 10; + protected const int RESEND_TIMEOUT = 4000; + protected const int MAX_SEQUENCE = 0xFFFFFF; + + public uint CircuitCode; + public EndPoint userEP; + + protected PacketServer m_networkServer; + + public ClientViewBase() + { + + } + + protected virtual void ProcessInPacket(Packet Pack) + { + + } + + protected virtual void ProcessOutPacket(Packet Pack) + { + // Keep track of when this packet was sent out + Pack.TickCount = Environment.TickCount; + + if (!Pack.Header.Resent) + { + // Set the sequence number + lock (SequenceLock) + { + if (Sequence >= MAX_SEQUENCE) + Sequence = 1; + else + Sequence++; + Pack.Header.Sequence = Sequence; + } + + if (Pack.Header.Reliable) //DIRTY HACK + { + lock (NeedAck) + { + if (!NeedAck.ContainsKey(Pack.Header.Sequence)) + { + try + { + NeedAck.Add(Pack.Header.Sequence, Pack); + } + catch (Exception e) // HACKY + { + e.ToString(); + // 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.Sequence + ") to the NeedAck dictionary for packet type " + + // packet.Type.ToString(), Helpers.LogLevel.Warning); + } + } + + // Don't append ACKs to resent packets, in case that's what was causing the + // delivery to fail + if (!Pack.Header.Resent) + { + // Append any ACKs that need to be sent out to this packet + lock (PendingAcks) + { + if (PendingAcks.Count > 0 && PendingAcks.Count < MAX_APPENDED_ACKS && + Pack.Type != PacketType.PacketAck && + Pack.Type != PacketType.LogoutRequest) + { + Pack.Header.AckList = new uint[PendingAcks.Count]; + int i = 0; + + foreach (uint ack in PendingAcks.Values) + { + Pack.Header.AckList[i] = ack; + i++; + } + + PendingAcks.Clear(); + Pack.Header.AppendedAcks = true; + } + } + } + } + } + + byte[] ZeroOutBuffer = new byte[4096]; + byte[] sendbuffer; + sendbuffer = Pack.ToBytes(); + + try + { + if (Pack.Header.Zerocoded) + { + int packetsize = Helpers.ZeroEncode(sendbuffer, sendbuffer.Length, ZeroOutBuffer); + m_networkServer.SendPacketTo(ZeroOutBuffer, packetsize, SocketFlags.None, CircuitCode);//userEP); + } + else + { + m_networkServer.SendPacketTo(sendbuffer, sendbuffer.Length, SocketFlags.None, CircuitCode); //userEP); + } + } + catch (Exception) + { + OpenSim.Framework.Console.MainConsole.Instance.WriteLine(OpenSim.Framework.Console.LogPriority.MEDIUM, "OpenSimClient.cs:ProcessOutPacket() - WARNING: Socket exception occurred on connection " + userEP.ToString() + " - killing thread"); + this.KillThread(); + } + + } + + public virtual void InPacket(Packet NewPack) + { + // Handle appended ACKs + if (NewPack.Header.AppendedAcks) + { + lock (NeedAck) + { + foreach (uint ack in NewPack.Header.AckList) + { + NeedAck.Remove(ack); + } + } + } + + // Handle PacketAck packets + if (NewPack.Type == PacketType.PacketAck) + { + PacketAckPacket ackPacket = (PacketAckPacket)NewPack; + + lock (NeedAck) + { + foreach (PacketAckPacket.PacketsBlock block in ackPacket.Packets) + { + NeedAck.Remove(block.ID); + } + } + } + else if ((NewPack.Type == PacketType.StartPingCheck)) + { + //reply to pingcheck + libsecondlife.Packets.StartPingCheckPacket startPing = (libsecondlife.Packets.StartPingCheckPacket)NewPack; + libsecondlife.Packets.CompletePingCheckPacket endPing = new CompletePingCheckPacket(); + endPing.PingID.PingID = startPing.PingID.PingID; + OutPacket(endPing); + } + else + { + QueItem item = new QueItem(); + item.Packet = NewPack; + item.Incoming = true; + this.PacketQueue.Enqueue(item); + } + + } + + public virtual void OutPacket(Packet NewPack) + { + QueItem item = new QueItem(); + item.Packet = NewPack; + item.Incoming = false; + this.PacketQueue.Enqueue(item); + } + + # region Low Level Packet Methods + + protected void ack_pack(Packet Pack) + { + if (Pack.Header.Reliable) + { + libsecondlife.Packets.PacketAckPacket ack_it = new PacketAckPacket(); + 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); + + } + /* + if (Pack.Header.Reliable) + { + lock (PendingAcks) + { + uint sequence = (uint)Pack.Header.Sequence; + if (!PendingAcks.ContainsKey(sequence)) { PendingAcks[sequence] = sequence; } + } + }*/ + } + + protected void ResendUnacked() + { + int now = Environment.TickCount; + + lock (NeedAck) + { + foreach (Packet packet in NeedAck.Values) + { + if ((now - packet.TickCount > RESEND_TIMEOUT) && (!packet.Header.Resent)) + { + OpenSim.Framework.Console.MainConsole.Instance.WriteLine(OpenSim.Framework.Console.LogPriority.VERBOSE, "Resending " + packet.Type.ToString() + " packet, " + + (now - packet.TickCount) + "ms have passed"); + + packet.Header.Resent = true; + OutPacket(packet); + } + } + } + } + + protected void SendAcks() + { + lock (PendingAcks) + { + if (PendingAcks.Count > 0) + { + if (PendingAcks.Count > 250) + { + // FIXME: Handle the odd case where we have too many pending ACKs queued up + OpenSim.Framework.Console.MainConsole.Instance.WriteLine(OpenSim.Framework.Console.LogPriority.VERBOSE, "Too many ACKs queued up!"); + return; + } + + //OpenSim.Framework.Console.MainConsole.Instance.WriteLine("Sending PacketAck"); + + + int i = 0; + PacketAckPacket acks = new PacketAckPacket(); + acks.Packets = new PacketAckPacket.PacketsBlock[PendingAcks.Count]; + + foreach (uint ack in PendingAcks.Values) + { + acks.Packets[i] = new PacketAckPacket.PacketsBlock(); + acks.Packets[i].ID = ack; + i++; + } + + acks.Header.Reliable = false; + OutPacket(acks); + + PendingAcks.Clear(); + } + } + } + + protected void AckTimer_Elapsed(object sender, ElapsedEventArgs ea) + { + SendAcks(); + ResendUnacked(); + } + #endregion + + protected virtual void KillThread() + { + + } + + #region Nested Classes + + public class QueItem + { + public QueItem() + { + } + + public Packet Packet; + public bool Incoming; + } + #endregion + } +} -- cgit v1.1