/// <summary> /// Default constructor /// </summary> /// <param name="agent">Reference to the client this packet is destined for</param> /// <param name="buffer">Serialized packet data. If the flags or sequence number /// need to be updated, they will be injected directly into this binary buffer</param> /// <param name="category">Throttling category for this packet</param> /// <param name="type">Packet type</param> public OutgoingPacket(LLAgent agent, UDPPacketBuffer buffer, ThrottleCategory category, PacketType type) { Agent = agent; Buffer = buffer; Category = category; Type = type; }
public void SendAcks(LLAgent agent) { const int MAX_ACKS_PER_PACKET = Byte.MaxValue; uint ack; if (agent.PendingAcks.TryDequeue(out ack)) { List <PacketAckPacket.PacketsBlock> blocks = new List <PacketAckPacket.PacketsBlock>(agent.PendingAcks.Count); PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock(); block.ID = ack; blocks.Add(block); int count = 1; while (count < MAX_ACKS_PER_PACKET && agent.PendingAcks.TryDequeue(out ack)) { block = new PacketAckPacket.PacketsBlock(); block.ID = ack; blocks.Add(block); ++count; } PacketAckPacket packet = new PacketAckPacket(); packet.Header.Reliable = false; packet.Packets = blocks.ToArray(); SendPacket(agent, packet, ThrottleCategory.Unknown, false); } }
private bool CheckForCameraMovement(ISceneEntity entity) { const float CAMERA_MOVE_THRESHOLD = 10f; if (entity is LLAgent) { LLAgent agent = (LLAgent)entity; // Calculate the center of the far frustum plane Vector3 camPosition = agent.CameraPosition + agent.CameraAtAxis * agent.DrawDistance; lock (m_lastCameraPositions) { Vector3 lastCamPos; if (!m_lastCameraPositions.TryGetValue(agent.LocalID, out lastCamPos)) { lastCamPos = Vector3.Zero; } if (Vector3.DistanceSquared(camPosition, lastCamPos) > CAMERA_MOVE_THRESHOLD * CAMERA_MOVE_THRESHOLD) { m_lastCameraPositions[entity.LocalID] = camPosition; return(true); } } } return(false); }
private void ChildAvatarUpdateHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response) { OSDMap requestMap = null; try { requestMap = OSDParser.Deserialize(request.Body) as OSDMap; } catch { } if (requestMap != null) { IScenePresence child; if (m_scene.TryGetPresence(requestMap["agent_id"].AsUUID(), out child) && child.IsChildPresence) { child.RelativePosition = requestMap["position"].AsVector3(); child.RelativeRotation = requestMap["rotation"].AsQuaternion(); if (child is LLAgent) { LLAgent childAgent = (LLAgent)child; childAgent.CameraPosition = requestMap["camera_center"].AsVector3(); childAgent.CameraAtAxis = requestMap["camera_at"].AsVector3(); childAgent.CameraLeftAxis = requestMap["camera_left"].AsVector3(); childAgent.CameraUpAxis = requestMap["camera_up"].AsVector3(); childAgent.DrawDistance = (float)requestMap["draw_distance"].AsReal(); } } } }
private void ClientOutgoingPacketHandler(LLAgent agent) { try { if (agent.IsConnected && agent.RemoteEndPoint != null) { if (m_resendUnacked) { ResendUnacked(agent); } if (m_sendAcks) { SendAcks(agent); } if (m_sendPing) { SendPing(agent); } // Dequeue any outgoing packets that are within the throttle limits if (agent.DequeueOutgoing()) { m_packetSent = true; } } } catch (Exception ex) { m_log.Error("OutgoingPacketHandler iteration for " + agent.ID + " threw an exception: " + ex.Message, ex); } }
private void PresenceAddHandler(object sender, PresenceArgs e) { m_scheduler.FireAndForget( delegate(object o) { // The default search distance (in every direction) for child agent connections const float NEIGHBOR_SEARCH_MARGIN = 256.0f; // HACK: Attempted fix for the viewer hanging indefinitely at login. It seems like // a client race condition when establishing neighbor connections too quickly System.Threading.Thread.Sleep(1000 * 5); if (e.Presence is LLAgent && !e.Presence.IsChildPresence) { LLAgent agent = (LLAgent)e.Presence; // Fetch nearby neighbors for the new presence // TODO: We should be doing this later, based off draw distance Vector3d globalPosition = e.Presence.Scene.MinPosition + new Vector3d(e.Presence.ScenePosition); SceneInfo[] nearNeighbors = m_scene.GetNeighborsNear(globalPosition, e.Presence.InterestRadius + NEIGHBOR_SEARCH_MARGIN); // Iterate over all of the given neighbors and send each a rez_avatar/request to create a child agent for (int i = 0; i < nearNeighbors.Length; i++) { SendRezAvatarRequest(agent, nearNeighbors[i], true); } } }, null ); }
private LLAgent CreateLLAgent(UserSession session, Vector3 startPosition, Vector3 lookAt, bool isChildAgent) { LLAgent client = new LLAgent(this, m_throttleRates, m_throttle, session.GetField("CircuitCode").AsUInteger(), session.User.ID, session.SessionID, session.SecureSessionID, null, m_defaultRTO, m_maxRTO, isChildAgent); // Set the verified flag client.IsVerified = (session.User.AccessLevel > 0); // Set the agent name client.Name = session.User.Name; // Set the starting position client.RelativePosition = startPosition; // Set the starting rotation lookAt.Z = 0.0f; Matrix4 lookAtMatrix = Matrix4.CreateLookAt(Vector3.Zero, lookAt, Vector3.UnitZ); client.RelativeRotation = lookAtMatrix.GetQuaternion(); m_clients.Add(client.ID, client.RemoteEndPoint, client); // Create a seed capability if (m_httpServer != null) { client.SeedCapability = this.Scene.Capabilities.AddCapability(session.User.ID, true, this.Scene.ID, "region_seed_capability"); } else { client.SeedCapability = new Uri("http://localhost:0"); } return(client); }
private void SendChildUpdate(IScenePresence presence) { const float DEFAULT_DRAW_DISTANCE = 128.0f; // Build the template child_avatar/update message OSDMap childUpdate = new OSDMap(); childUpdate["agent_id"] = OSD.FromUUID(presence.ID); childUpdate["rotation"] = OSD.FromQuaternion(presence.SceneRotation); float drawDistance = DEFAULT_DRAW_DISTANCE; if (presence is LLAgent) { LLAgent agent = (LLAgent)presence; drawDistance = agent.DrawDistance; childUpdate["camera_center"] = OSD.FromVector3(agent.CameraPosition); childUpdate["camera_at"] = OSD.FromVector3(agent.CameraAtAxis); childUpdate["camera_left"] = OSD.FromVector3(agent.CameraLeftAxis); childUpdate["camera_up"] = OSD.FromVector3(agent.CameraUpAxis); } childUpdate["draw_distance"] = OSD.FromReal(drawDistance); // Get a list of neighbors to send this update to based on the draw distance SceneInfo[] neighbors = m_scene.GetNeighborsNear(m_scene.MinPosition + new Vector3d(presence.ScenePosition), drawDistance); for (int i = 0; i < neighbors.Length; i++) { SceneInfo neighbor = neighbors[i]; // Find the presence position relative to this neighbor Vector3 relativePosition = presence.ScenePosition - new Vector3(neighbor.MinPosition - presence.Scene.MinPosition); childUpdate["position"] = OSD.FromVector3(relativePosition); Uri childUpdateCap; if (neighbor.TryGetCapability("child_avatar/update", out childUpdateCap)) { try { // Send the message //m_log.Debug("Sending child agent update for " + presence.Name); string message = OSDParser.SerializeJsonString(childUpdate); UntrustedHttpWebRequest.PostToUntrustedUrl(childUpdateCap, message); } catch (Exception ex) { m_log.Warn("child_avatar/update from " + m_scene.Name + " to " + neighbor.Name + " for agent " + presence.Name + " failed: " + ex.Message); } } else { // This shouldn't happen since we check for the child_avatar/update capability // before adding this agent/neighbor pair to the queue throw new InvalidOperationException("child_avatar/update capability not found in SendChildUpdate handler"); } } }
public void CompletePing(LLAgent agent, byte pingID) { CompletePingCheckPacket completePing = new CompletePingCheckPacket(); completePing.Header.Reliable = false; completePing.PingID.PingID = pingID; SendPacket(agent, completePing, ThrottleCategory.Unknown, false); }
private bool SendRezAvatarRequest(LLAgent agent, SceneInfo neighbor, bool isChild) { IPAddress simHost; int simPort; Uri seedCapability; return(SendRezAvatarRequest(agent, neighbor, isChild, out simHost, out simPort, out seedCapability)); }
public void FireQueueEmpty(LLAgent agent, ThrottleCategoryFlags categories) { QueueEmptyCallback callback = OnQueueEmpty; if (callback != null) { callback(agent, categories); } }
private bool SendRezAvatarRequest(LLAgent agent, SceneInfo neighbor, bool isChild, out IPAddress simHost, out int simPort, out Uri seedCapability) { simHost = null; simPort = 0; seedCapability = null; Uri rezAvatarRequestCap; if (neighbor.TryGetCapability("rez_avatar/request", out rezAvatarRequestCap)) { string firstName, lastName; Util.GetFirstLastName(agent.Name, out firstName, out lastName); // Find the presence position relative to this neighbor Vector3 relativePosition = agent.ScenePosition - new Vector3(neighbor.MinPosition - m_scene.MinPosition); // Calculate the direction this agent is currently facing Vector3 lookAt = Vector3.UnitY * agent.RelativeRotation; // Create the template rez_avatar/request message OSDMap rezAvatarRequest = new OSDMap { { "agent_id", OSD.FromUUID(agent.ID) }, { "session_id", OSD.FromUUID(agent.SessionID) }, { "position", OSD.FromVector3(relativePosition) }, { "look_at", OSD.FromVector3(lookAt) }, { "velocity", OSD.FromVector3(agent.Velocity) }, { "child", OSD.FromBoolean(isChild) } }; OSDMap rezAvatarResponse = null; try { // Send the message and get a response rezAvatarResponse = OSDParser.Deserialize(UntrustedHttpWebRequest.PostToUntrustedUrl( rezAvatarRequestCap, OSDParser.SerializeJsonString(rezAvatarRequest))) as OSDMap; } catch { } if (rezAvatarResponse != null) { return(RezChildAgentReplyHandler(agent, rezAvatarResponse, out simHost, out simPort, out seedCapability)); } else { m_log.Warn(m_scene.Name + " failed to create a child agent on " + neighbor.Name + ", rez_avatar/request failed"); } } else { m_log.Warn(neighbor.Name + " does not have a rez_avatar/request capability"); } return(false); }
public void SendPing(LLAgent agent) { StartPingCheckPacket pc = new StartPingCheckPacket(); pc.Header.Reliable = false; pc.PingID.PingID = (byte)agent.CurrentPingSequence++; // We *could* get OldestUnacked, but it would hurt performance and not provide any benefit pc.PingID.OldestUnacked = 0; SendPacket(agent, pc, ThrottleCategory.Unknown, false); }
private void EventQueueManagerThread() { while (m_running) { bool doSleep = true; m_scene.ForEachPresence( delegate(IScenePresence presence) { if (!(presence is LLAgent) || presence.InterestList == null) { return; } LLAgent agent = (LLAgent)presence; if (agent.EventQueue.ConnectionOpen) { if (agent.EventQueue.EventQueue.Count > 0) { // Set ConnectionOpen to false early so we don't try to send // events on this EQ again before the first call finishes agent.EventQueue.ConnectionOpen = false; m_scheduler.FireAndForget(agent.EventQueue.SendEvents, null); doSleep = false; } else { // Check for a timeout int elapsed = Util.TickCount() - agent.EventQueue.StartTime; if (elapsed >= CONNECTION_TIMEOUT) { //m_log.DebugFormat("{0}ms passed without an event, timing out the event queue", elapsed); agent.EventQueue.SendResponse(null); doSleep = false; } } } } ); if (doSleep) { Thread.Sleep(Simian.LONG_SLEEP_INTERVAL); } m_scheduler.ThreadKeepAlive(); } m_scheduler.RemoveThread(); }
public void ResendUnacked(LLAgent agent) { //FIXME: Make this an .ini setting const int AGENT_TIMEOUT_MS = 1000 * 60; // Disconnect an agent if no packets are received for some time if (Util.TickCount() - agent.TickLastPacketReceived > AGENT_TIMEOUT_MS) { m_log.Warn("Ack timeout, disconnecting " + agent.ID); ShutdownClient(agent); return; } // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO List <OutgoingPacket> expiredPackets = agent.NeedAcks.GetExpiredPackets(agent.RTO); if (expiredPackets != null) { //m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO); // Exponential backoff of the retransmission timeout agent.BackoffRTO(); // Resend packets for (int i = 0; i < expiredPackets.Count; i++) { OutgoingPacket outgoingPacket = expiredPackets[i]; //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); // Set the resent flag outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); outgoingPacket.Category = ThrottleCategory.Resend; // Bump up the resend count on this packet Interlocked.Increment(ref outgoingPacket.ResendCount); Interlocked.Increment(ref m_packetsResent); // Requeue or resend the packet if (!agent.EnqueueOutgoing(outgoingPacket)) { SendPacketFinal(outgoingPacket); } } } }
private void PacketDelegate(object state) { PacketCallbackWrapper wrapper = (PacketCallbackWrapper)state; IncomingPacket incomingPacket = wrapper.IncomingPacket; LLAgent agent = wrapper.IncomingPacket.Agent; if (!agent.IsConnected) { m_log.DebugFormat("Dropping incoming {0} packet (#{1}) for dead client {2}", incomingPacket.Packet.Type, incomingPacket.Packet.Header.ID, agent.ID); return; } try { wrapper.Callback(incomingPacket.Packet, agent); } catch (Exception ex) { m_log.Error("Async Packet Event Handler: " + ex.Message, ex); } // TODO: Optionally log timing info for this packet int now = Util.TickCount(); int recvTime = incomingPacket.StartedHandling - incomingPacket.Received; if (recvTime > 1000) { m_log.Warn("Spent " + recvTime + "ms receiving " + incomingPacket.Packet.Type + " packet for " + wrapper.IncomingPacket.Agent); } int processTime = now - incomingPacket.StartedHandling; if (processTime > 1000) { m_log.Warn("Spent " + processTime + "ms processing " + incomingPacket.Packet.Type + " packet for " + incomingPacket.Agent); } }
private bool BorderCrossLLAgent(LLAgent agent, SceneInfo neighbor) { IPAddress simHost; int simPort; Uri seedCapability; // Send a rez_avatar/request message to create a root agent or upgrade a child agent if (SendRezAvatarRequest(agent, neighbor, false, out simHost, out simPort, out seedCapability)) { // Demote this agent to a child agent agent.IsChildPresence = true; // Calculate our position relative to the new region Vector3 relativePosition = agent.ScenePosition - new Vector3(neighbor.MinPosition - m_scene.MinPosition); // Send the CrossedRegion message over the event queue to the client CrossedRegionMessage crossed = new CrossedRegionMessage(); crossed.AgentID = agent.ID; crossed.SessionID = agent.SessionID; crossed.RegionHandle = Util.PositionToRegionHandle(neighbor.MinPosition); crossed.IP = simHost; crossed.Port = simPort; crossed.Position = relativePosition; crossed.LookAt = agent.CameraAtAxis; // TODO: Get LookAt from the agent's rotation crossed.SeedCapability = seedCapability; m_log.Info("Sending CrossedRegion to " + agent.Name + " from " + m_scene.Name + " to " + neighbor.Name + ", pos=" + relativePosition + ", vel=" + agent.Velocity); agent.EventQueue.QueueEvent("CrossedRegion", crossed.Serialize()); return(true); } else { m_log.Warn("Border crossing " + agent.Name + " from " + m_scene.Name + " to " + neighbor.Name + " failed, rez_avatar/request did not succeed"); return(false); } }
private void PresenceRemoveHandler(object sender, PresenceArgs e) { if (e.Presence is LLAgent) { // Remove all capabilities associated with this client if (m_httpServer != null) { this.Scene.Capabilities.RemoveCapabilities(e.Presence.ID); } // Remove the UDP client reference LLAgent agent = (LLAgent)e.Presence; if (m_clients.Remove(agent.ID, agent.RemoteEndPoint)) { m_log.Debug("Removed client reference from the LLUDP server"); } } else { m_log.Warn("PresenceRemoveHandler called for non-LLAgent: " + e.Presence); } }
public void SendPacket(LLAgent agent, Packet packet, ThrottleCategory category, bool allowSplitting) { // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way if ((packet.Type == PacketType.CoarseLocationUpdate || packet.Type == PacketType.AvatarGroupsReply) && allowSplitting) { allowSplitting = false; } try { if (allowSplitting && packet.HasVariableBlocks) { byte[][] datas = packet.ToBytesMultiple(); int packetCount = datas.Length; if (packetCount < 1) { m_log.Error("[LLUDPSERVER]: Failed to split " + packet.Type + " with estimated length " + packet.Length); } for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; SendPacketData(agent, data, packet.Type, category); } } else { byte[] data = packet.ToBytes(); SendPacketData(agent, data, packet.Type, category); } } catch (NullReferenceException) { System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(1, true); m_log.Error("An invalid " + packet.Type + " packet was built in:" + Environment.NewLine + trace.ToString()); } }
public void SendPacket(LLAgent agent, Packet packet, ThrottleCategory category, bool allowSplitting) { m_udpServer.SendPacket(agent, packet, category, allowSplitting); }
private bool SendRezAvatarRequest(LLAgent agent, SceneInfo neighbor, bool isChild) { IPAddress simHost; int simPort; Uri seedCapability; return SendRezAvatarRequest(agent, neighbor, isChild, out simHost, out simPort, out seedCapability); }
private bool BorderCrossLLAgent(LLAgent agent, SceneInfo neighbor) { IPAddress simHost; int simPort; Uri seedCapability; // Send a rez_avatar/request message to create a root agent or upgrade a child agent if (SendRezAvatarRequest(agent, neighbor, false, out simHost, out simPort, out seedCapability)) { // Demote this agent to a child agent agent.IsChildPresence = true; // Calculate our position relative to the new region Vector3 relativePosition = agent.ScenePosition - new Vector3(neighbor.MinPosition - m_scene.MinPosition); // Send the CrossedRegion message over the event queue to the client CrossedRegionMessage crossed = new CrossedRegionMessage(); crossed.AgentID = agent.ID; crossed.SessionID = agent.SessionID; crossed.RegionHandle = Util.PositionToRegionHandle(neighbor.MinPosition); crossed.IP = simHost; crossed.Port = simPort; crossed.Position = relativePosition; crossed.LookAt = agent.CameraAtAxis; // TODO: Get LookAt from the agent's rotation crossed.SeedCapability = seedCapability; m_log.Info("Sending CrossedRegion to " + agent.Name + " from " + m_scene.Name + " to " + neighbor.Name + ", pos=" + relativePosition + ", vel=" + agent.Velocity); agent.EventQueue.QueueEvent("CrossedRegion", crossed.Serialize()); return true; } else { m_log.Warn("Border crossing " + agent.Name + " from " + m_scene.Name + " to " + neighbor.Name + " failed, rez_avatar/request did not succeed"); return false; } }
private LLAgent CreateLLAgent(UserSession session, Vector3 startPosition, Vector3 lookAt, bool isChildAgent) { LLAgent client = new LLAgent(this, m_throttleRates, m_throttle, session.GetField("CircuitCode").AsUInteger(), session.User.ID, session.SessionID, session.SecureSessionID, null, m_defaultRTO, m_maxRTO, isChildAgent); // Set the verified flag client.IsVerified = (session.User.AccessLevel > 0); // Set the agent name client.Name = session.User.Name; // Set the starting position client.RelativePosition = startPosition; // Set the starting rotation lookAt.Z = 0.0f; Matrix4 lookAtMatrix = Matrix4.CreateLookAt(Vector3.Zero, lookAt, Vector3.UnitZ); client.RelativeRotation = lookAtMatrix.GetQuaternion(); m_clients.Add(client.ID, client.RemoteEndPoint, client); // Create a seed capability if (m_httpServer != null) client.SeedCapability = this.Scene.Capabilities.AddCapability(session.User.ID, true, this.Scene.ID, "region_seed_capability"); else client.SeedCapability = new Uri("http://localhost:0"); return client; }
public bool TryGetAgent(UUID agentID, out LLAgent agent) { return m_clients.TryGetValue(agentID, out agent); }
private void ShutdownClient(LLAgent agent) { // Remove this client from the scene agent.Shutdown(); m_clients.Remove(agent.ID, agent.RemoteEndPoint); }
public void SendPacketData(LLAgent agent, byte[] data, PacketType type, ThrottleCategory category) { m_udpServer.SendPacketData(agent, data, type, category); }
public bool TryGetAgent(UUID agentID, out LLAgent agent) { return(m_clients.TryGetValue(agentID, out agent)); }
private void EventQueueHandler(Capability cap, IHttpClientContext context, IHttpRequest request, IHttpResponse response) { // Decode the request OSD osdRequest = null; try { osdRequest = OSDParser.Deserialize(request.Body); } catch (Exception) { } if (request != null && osdRequest.Type == OSDType.Map) { OSDMap requestMap = (OSDMap)osdRequest; int ack = requestMap["ack"].AsInteger(); bool done = requestMap["done"].AsBoolean(); LLAgent agent = null; // Fetch an agent reference from either the scene or the LLUDP stack (since the // presence might not exist in the scene yet) IScenePresence presence; if (m_scene.TryGetPresence(cap.OwnerID, out presence) && presence is LLAgent) { agent = (LLAgent)presence; } else { m_udp.TryGetAgent(cap.OwnerID, out agent); } if (agent != null) { if (agent.EventQueue.ConnectionOpen) { m_log.Debug("New connection opened to the event queue for " + agent.Name + " while a previous connection is open. Closing old connection"); // If the old connection is still open, queue a signal to close it. Otherwise, just wait for the closed // connection to be detected by the handler thread if (agent.EventQueue.Response != null) { agent.EventQueue.EventQueue.Enqueue(null); } while (agent.EventQueue.ConnectionOpen) { Thread.Sleep(50); } m_log.Debug("Old event queue connection closed for " + agent.Name); } if (!done) { //m_log.Debug("Opening event queue connection for " + agent.Name); agent.EventQueue.Context = context; agent.EventQueue.Request = request; agent.EventQueue.Response = response; agent.EventQueue.StartTime = Util.TickCount(); agent.EventQueue.ConnectionOpen = true; // ACK sanity checking if (ack != agent.EventQueue.CurrentID - 1 && ack != 0) { m_log.WarnFormat("Received an ack for id {0}, last id sent was {1}", ack, agent.EventQueue.CurrentID - 1); } } else { m_log.DebugFormat("Shutting down the event queue {0} for {1} at the client's request", request.Uri, agent.Name); agent.EventQueue.SendEvents(50); } } else { m_log.Warn("Received an event queue connection from client " + cap.OwnerID + " that does not have a presence in scene " + m_scene.Name); } } else { m_log.Warn("Received a request with invalid or missing data at " + request.Uri + ", closing the connection"); response.Connection = request.Connection; response.Status = System.Net.HttpStatusCode.BadRequest; response.Send(); } }
public void SendPacketData(LLAgent agent, byte[] data, PacketType type, ThrottleCategory category) { int dataLength = data.Length; bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; bool doCopy = true; // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here // to accomodate for both common scenarios and provide ample room for ACK appending in both int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; UDPPacketBuffer buffer = new UDPPacketBuffer(agent.RemoteEndPoint, bufferSize); // Zerocode if needed if (doZerocode) { try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); doCopy = false; } catch (IndexOutOfRangeException) { // The packet grew larger than the bufferSize while zerocoding. // Remove the MSG_ZEROCODED flag and send the unencoded data // instead m_log.Debug("Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength + " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag"); data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); } } // If the packet data wasn't already copied during zerocoding, copy it now if (doCopy) { if (dataLength > buffer.Data.Length) { m_log.Error("Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length); buffer.Data = new byte[dataLength]; } Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } buffer.DataLength = dataLength; #region Queue or Send OutgoingPacket outgoingPacket = new OutgoingPacket(agent, buffer, category, type); if (!agent.EnqueueOutgoing(outgoingPacket)) { SendPacketFinal(outgoingPacket); } #endregion Queue or Send }
internal void SendPacketFinal(OutgoingPacket outgoingPacket) { const int MAX_APPENDED_ACKS = 250; UDPPacketBuffer buffer = outgoingPacket.Buffer; byte flags = buffer.Data[0]; bool isResend = (flags & Helpers.MSG_RESENT) != 0; bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; bool isZerocoded = (flags & Helpers.MSG_ZEROCODED) != 0; LLAgent agent = outgoingPacket.Agent; if (!agent.IsConnected || buffer.RemoteEndPoint == null) { m_log.Debug("Dropping " + buffer.DataLength + " byte packet to client we have no route to"); return; } int dataLength = buffer.DataLength; #region ACK Appending // NOTE: I'm seeing problems with some viewers when ACKs are appended to zerocoded packets so I've disabled that here if (!isZerocoded && outgoingPacket.Type != PacketType.PacketAck) { // Keep appending ACKs until there is no room left in the buffer or there are // no more ACKs to append byte ackCount = 0; uint ack; while (dataLength + 5 < buffer.Data.Length && ackCount < MAX_APPENDED_ACKS && agent.PendingAcks.TryDequeue(out ack)) { Utils.UIntToBytesBig(ack, buffer.Data, dataLength); dataLength += 4; ++ackCount; } if (ackCount > 0) { // Set the last byte of the packet equal to the number of appended ACKs buffer.Data[dataLength++] = ackCount; // Set the appended ACKs flag on this packet buffer.Data[0] |= Helpers.MSG_APPENDED_ACKS; } } #endregion ACK Appending buffer.DataLength = dataLength; #region Sequence Number Assignment if (!isResend) { // Not a resend, assign a new sequence number uint sequenceNumber = (uint)Interlocked.Increment(ref agent.CurrentSequence); Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); outgoingPacket.SequenceNumber = sequenceNumber; if (isReliable) { // Add this packet to the list of ACK responses we are waiting on from the server agent.NeedAcks.Add(outgoingPacket); } } #endregion Sequence Number Assignment // Stats tracking Interlocked.Increment(ref agent.PacketsSent); Interlocked.Increment(ref m_packetsSent); if (isReliable) { Interlocked.Add(ref agent.UnackedBytes, outgoingPacket.Buffer.DataLength); } if (LoggingEnabled) { m_log.Debug("<-- (" + buffer.RemoteEndPoint + ") " + outgoingPacket.Type); } // Put the UDP payload on the wire Send(buffer); // Keep track of when this packet was sent out (right now) outgoingPacket.TickCount = Util.TickCount(); //m_log.Debug("Sent " + outgoingPacket.Buffer.DataLength + " byte " + outgoingPacket.Category + " packet"); }
public bool TryGetAgent(UUID agentID, out LLAgent agent) { return m_udpServer.TryGetAgent(agentID, out agent); }
private void ClientOutgoingPacketHandler(LLAgent agent) { try { if (agent.IsConnected && agent.RemoteEndPoint != null) { if (m_resendUnacked) ResendUnacked(agent); if (m_sendAcks) SendAcks(agent); if (m_sendPing) SendPing(agent); // Dequeue any outgoing packets that are within the throttle limits if (agent.DequeueOutgoing()) m_packetSent = true; } } catch (Exception ex) { m_log.Error("OutgoingPacketHandler iteration for " + agent.ID + " threw an exception: " + ex.Message, ex); } }
/// <summary> /// Default constructor /// </summary> /// <param name="agent">Reference to the client this packet came from</param> /// <param name="packet">Packet data</param> /// <param name="received">Tick count when this packet was received</param> public IncomingPacket(LLAgent agent, Packet packet, int received) { Agent = agent; Packet = packet; Received = received; }
public void FireQueueEmpty(LLAgent agent, ThrottleCategoryFlags categories) { m_udp.FireQueueEmpty(agent, categories); }
private bool RezChildAgentReplyHandler(LLAgent agent, OSDMap map, out IPAddress simHost, out int simPort, out Uri seedCapability) { simHost = null; simPort = 0; seedCapability = null; if (map["connect"].AsBoolean()) { #region Response Parsing string simHostStr = map["sim_host"].AsString(); if (!IPAddress.TryParse(simHostStr, out simHost)) { m_log.Warn("rez_avatar/response had an invalid sim_host: " + simHostStr); return false; } simPort = map["sim_port"].AsInteger(); UUID regionID = map["region_id"].AsUUID(); uint regionX = map["region_x"].AsUInteger(); uint regionY = map["region_y"].AsUInteger(); ulong regionHandle = Utils.UIntsToLong(regionX, regionY); seedCapability = map["region_seed_capability"].AsUri(); if (seedCapability == null) { m_log.Warn("rez_avatar/response had an invalid region_seed_capability: " + map["region_seed_capability"].AsString()); return false; } #endregion Response Parsing #region EnableSimulator EnableSimulatorMessage.SimulatorInfoBlock block = new EnableSimulatorMessage.SimulatorInfoBlock(); block.IP = simHost; block.Port = simPort; block.RegionHandle = regionHandle; EnableSimulatorMessage enable = new EnableSimulatorMessage(); enable.Simulators = new EnableSimulatorMessage.SimulatorInfoBlock[1]; enable.Simulators[0] = block; m_log.Debug("Sending EnableSimulator message for scene " + regionID + " to " + agent.Name); agent.EventQueue.QueueEvent("EnableSimulator", enable.Serialize()); #endregion EnableSimulator #region EstablishAgentCommunication // Send an EstablishAgentCommunication event down to the client to get the neighbor event queue established EstablishAgentCommunicationMessage eacMessage = new EstablishAgentCommunicationMessage(); eacMessage.AgentID = regionID; eacMessage.Address = simHost; eacMessage.Port = simPort; eacMessage.SeedCapability = seedCapability; m_log.Debug("Sending EstablishAgentCommunication message for seedcap " + seedCapability + " to " + agent.Name); agent.EventQueue.QueueEvent("EstablishAgentCommunication", eacMessage.Serialize()); #endregion EstablishAgentCommunication return true; } else { m_log.Warn("rez_avatar/request from " + m_scene.Name + " for child agent " + agent.Name + " failed: " + map["message"].AsString()); return false; } }
public void ResendUnacked(LLAgent agent) { //FIXME: Make this an .ini setting const int AGENT_TIMEOUT_MS = 1000 * 60; // Disconnect an agent if no packets are received for some time if (Util.TickCount() - agent.TickLastPacketReceived > AGENT_TIMEOUT_MS) { m_log.Warn("Ack timeout, disconnecting " + agent.ID); ShutdownClient(agent); return; } // Get a list of all of the packets that have been sitting unacked longer than udpClient.RTO List<OutgoingPacket> expiredPackets = agent.NeedAcks.GetExpiredPackets(agent.RTO); if (expiredPackets != null) { //m_log.Debug("[LLUDPSERVER]: Resending " + expiredPackets.Count + " packets to " + udpClient.AgentID + ", RTO=" + udpClient.RTO); // Exponential backoff of the retransmission timeout agent.BackoffRTO(); // Resend packets for (int i = 0; i < expiredPackets.Count; i++) { OutgoingPacket outgoingPacket = expiredPackets[i]; //m_log.DebugFormat("[LLUDPSERVER]: Resending packet #{0} (attempt {1}), {2}ms have passed", // outgoingPacket.SequenceNumber, outgoingPacket.ResendCount, Environment.TickCount - outgoingPacket.TickCount); // Set the resent flag outgoingPacket.Buffer.Data[0] = (byte)(outgoingPacket.Buffer.Data[0] | Helpers.MSG_RESENT); outgoingPacket.Category = ThrottleCategory.Resend; // Bump up the resend count on this packet Interlocked.Increment(ref outgoingPacket.ResendCount); Interlocked.Increment(ref m_packetsResent); // Requeue or resend the packet if (!agent.EnqueueOutgoing(outgoingPacket)) SendPacketFinal(outgoingPacket); } } }
private bool SendRezAvatarRequest(LLAgent agent, SceneInfo neighbor, bool isChild, out IPAddress simHost, out int simPort, out Uri seedCapability) { simHost = null; simPort = 0; seedCapability = null; Uri rezAvatarRequestCap; if (neighbor.TryGetCapability("rez_avatar/request", out rezAvatarRequestCap)) { string firstName, lastName; Util.GetFirstLastName(agent.Name, out firstName, out lastName); // Find the presence position relative to this neighbor Vector3 relativePosition = agent.ScenePosition - new Vector3(neighbor.MinPosition - m_scene.MinPosition); // Calculate the direction this agent is currently facing Vector3 lookAt = Vector3.UnitY * agent.RelativeRotation; // Create the template rez_avatar/request message OSDMap rezAvatarRequest = new OSDMap { { "agent_id", OSD.FromUUID(agent.ID) }, { "session_id", OSD.FromUUID(agent.SessionID) }, { "position", OSD.FromVector3(relativePosition) }, { "look_at", OSD.FromVector3(lookAt) }, { "velocity", OSD.FromVector3(agent.Velocity) }, { "child", OSD.FromBoolean(isChild) } }; OSDMap rezAvatarResponse = null; try { // Send the message and get a response rezAvatarResponse = OSDParser.Deserialize(UntrustedHttpWebRequest.PostToUntrustedUrl( rezAvatarRequestCap, OSDParser.SerializeJsonString(rezAvatarRequest))) as OSDMap; } catch { } if (rezAvatarResponse != null) { return RezChildAgentReplyHandler(agent, rezAvatarResponse, out simHost, out simPort, out seedCapability); } else { m_log.Warn(m_scene.Name + " failed to create a child agent on " + neighbor.Name + ", rez_avatar/request failed"); } } else { m_log.Warn(neighbor.Name + " does not have a rez_avatar/request capability"); } return false; }
public void SendAcks(LLAgent agent) { const int MAX_ACKS_PER_PACKET = Byte.MaxValue; uint ack; if (agent.PendingAcks.TryDequeue(out ack)) { List<PacketAckPacket.PacketsBlock> blocks = new List<PacketAckPacket.PacketsBlock>(agent.PendingAcks.Count); PacketAckPacket.PacketsBlock block = new PacketAckPacket.PacketsBlock(); block.ID = ack; blocks.Add(block); int count = 1; while (count < MAX_ACKS_PER_PACKET && agent.PendingAcks.TryDequeue(out ack)) { block = new PacketAckPacket.PacketsBlock(); block.ID = ack; blocks.Add(block); ++count; } PacketAckPacket packet = new PacketAckPacket(); packet.Header.Reliable = false; packet.Packets = blocks.ToArray(); SendPacket(agent, packet, ThrottleCategory.Unknown, false); } }
public bool TryGetAgent(UUID agentID, out LLAgent agent) { return(m_udpServer.TryGetAgent(agentID, out agent)); }
public void SendPacket(LLAgent agent, Packet packet, ThrottleCategory category, bool allowSplitting) { // CoarseLocationUpdate and AvatarGroupsReply packets cannot be split in an automated way if ((packet.Type == PacketType.CoarseLocationUpdate || packet.Type == PacketType.AvatarGroupsReply) && allowSplitting) allowSplitting = false; try { if (allowSplitting && packet.HasVariableBlocks) { byte[][] datas = packet.ToBytesMultiple(); int packetCount = datas.Length; if (packetCount < 1) m_log.Error("[LLUDPSERVER]: Failed to split " + packet.Type + " with estimated length " + packet.Length); for (int i = 0; i < packetCount; i++) { byte[] data = datas[i]; SendPacketData(agent, data, packet.Type, category); } } else { byte[] data = packet.ToBytes(); SendPacketData(agent, data, packet.Type, category); } } catch (NullReferenceException) { System.Diagnostics.StackTrace trace = new System.Diagnostics.StackTrace(1, true); m_log.Error("An invalid " + packet.Type + " packet was built in:" + Environment.NewLine + trace.ToString()); } }
private void PacketHandler() { Action <LLAgent> clientPacketHandler = ClientOutgoingPacketHandler; while (base.IsRunning) { try { // Time keeping int now = Util.TickCount(); int elapsed = now - m_tickLastOutgoingPacketHandler; m_tickLastOutgoingPacketHandler = now; // Maximum time to wait dequeuing an incoming packet. Used // to put this thread to sleep when there is little or no // activity int dequeueTimeout = 0; #region Outgoing Packets m_packetSent = false; #region Update Timers m_resendUnacked = false; m_sendAcks = false; m_sendPing = false; m_resendTimer.Elapsed += elapsed; m_ackTimer.Elapsed += elapsed; m_pingTimer.Elapsed += elapsed; if (m_resendTimer.Elapsed >= m_resendTimer.Interval) { m_resendUnacked = true; m_resendTimer.Elapsed = 0; } if (m_ackTimer.Elapsed >= m_ackTimer.Interval) { m_sendAcks = true; m_ackTimer.Elapsed = 0; } if (m_pingTimer.Elapsed >= m_pingTimer.Interval) { m_sendPing = true; m_pingTimer.Elapsed = 0; } #endregion Update Timers // Handle outgoing packets, resends, acknowledgements, and pings for each // client. m_packetSent will be set to true if a packet is sent m_clients.ForEach(clientPacketHandler); // If nothing was sent, wait up to the minimum amount of time before a // token bucket could get more tokens, if we have clients connected. // Otherwise, do a long wait if (!m_packetSent) { if (m_clients.Count > 0) { dequeueTimeout = (int)Scene.Simian.TickCountResolution; } else { dequeueTimeout = Simian.LONG_SLEEP_INTERVAL; } } #endregion Outgoing Packets #region Incoming Packets IncomingPacket incomingPacket = null; if (m_packetInbox.Dequeue(dequeueTimeout, ref incomingPacket)) { Packet packet = incomingPacket.Packet; LLAgent agent = incomingPacket.Agent; // Record the time we started processing this packet incomingPacket.StartedHandling = Util.TickCount(); // Sanity check if (packet == null || agent == null) { m_log.WarnFormat("Processing a packet with incomplete state. Packet=\"{0}\", LLAgent=\"{1}\"", packet, agent); } PacketEvents.BeginRaiseEvent(incomingPacket); } #endregion Incoming Packets } catch (Exception ex) { m_log.Error("Error in the packet handler loop: " + ex.Message, ex); } Scheduler.ThreadKeepAlive(); } if (m_packetInbox.Count > 0) { m_log.Warn("IncomingPacketHandler is shutting down, dropping " + m_packetInbox.Count + " packets"); } m_packetInbox.Clear(); Scheduler.RemoveThread(); }
public void SendPacketData(LLAgent agent, byte[] data, PacketType type, ThrottleCategory category) { int dataLength = data.Length; bool doZerocode = (data[0] & Helpers.MSG_ZEROCODED) != 0; bool doCopy = true; // Frequency analysis of outgoing packet sizes shows a large clump of packets at each end of the spectrum. // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here // to accomodate for both common scenarios and provide ample room for ACK appending in both int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; UDPPacketBuffer buffer = new UDPPacketBuffer(agent.RemoteEndPoint, bufferSize); // Zerocode if needed if (doZerocode) { try { dataLength = Helpers.ZeroEncode(data, dataLength, buffer.Data); doCopy = false; } catch (IndexOutOfRangeException) { // The packet grew larger than the bufferSize while zerocoding. // Remove the MSG_ZEROCODED flag and send the unencoded data // instead m_log.Debug("Packet exceeded buffer size during zerocoding for " + type + ". DataLength=" + dataLength + " and BufferLength=" + buffer.Data.Length + ". Removing MSG_ZEROCODED flag"); data[0] = (byte)(data[0] & ~Helpers.MSG_ZEROCODED); } } // If the packet data wasn't already copied during zerocoding, copy it now if (doCopy) { if (dataLength > buffer.Data.Length) { m_log.Error("Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length); buffer.Data = new byte[dataLength]; } Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } buffer.DataLength = dataLength; #region Queue or Send OutgoingPacket outgoingPacket = new OutgoingPacket(agent, buffer, category, type); if (!agent.EnqueueOutgoing(outgoingPacket)) SendPacketFinal(outgoingPacket); #endregion Queue or Send }
public void FireQueueEmpty(LLAgent agent, ThrottleCategoryFlags categories) { QueueEmptyCallback callback = OnQueueEmpty; if (callback != null) callback(agent, categories); }
private bool RezChildAgentReplyHandler(LLAgent agent, OSDMap map, out IPAddress simHost, out int simPort, out Uri seedCapability) { simHost = null; simPort = 0; seedCapability = null; if (map["connect"].AsBoolean()) { #region Response Parsing string simHostStr = map["sim_host"].AsString(); if (!IPAddress.TryParse(simHostStr, out simHost)) { m_log.Warn("rez_avatar/response had an invalid sim_host: " + simHostStr); return(false); } simPort = map["sim_port"].AsInteger(); UUID regionID = map["region_id"].AsUUID(); uint regionX = map["region_x"].AsUInteger(); uint regionY = map["region_y"].AsUInteger(); ulong regionHandle = Utils.UIntsToLong(regionX, regionY); seedCapability = map["region_seed_capability"].AsUri(); if (seedCapability == null) { m_log.Warn("rez_avatar/response had an invalid region_seed_capability: " + map["region_seed_capability"].AsString()); return(false); } #endregion Response Parsing #region EnableSimulator EnableSimulatorMessage.SimulatorInfoBlock block = new EnableSimulatorMessage.SimulatorInfoBlock(); block.IP = simHost; block.Port = simPort; block.RegionHandle = regionHandle; EnableSimulatorMessage enable = new EnableSimulatorMessage(); enable.Simulators = new EnableSimulatorMessage.SimulatorInfoBlock[1]; enable.Simulators[0] = block; m_log.Debug("Sending EnableSimulator message for scene " + regionID + " to " + agent.Name); agent.EventQueue.QueueEvent("EnableSimulator", enable.Serialize()); #endregion EnableSimulator #region EstablishAgentCommunication // Send an EstablishAgentCommunication event down to the client to get the neighbor event queue established EstablishAgentCommunicationMessage eacMessage = new EstablishAgentCommunicationMessage(); eacMessage.AgentID = regionID; eacMessage.Address = simHost; eacMessage.Port = simPort; eacMessage.SeedCapability = seedCapability; m_log.Debug("Sending EstablishAgentCommunication message for seedcap " + seedCapability + " to " + agent.Name); agent.EventQueue.QueueEvent("EstablishAgentCommunication", eacMessage.Serialize()); #endregion EstablishAgentCommunication return(true); } else { m_log.Warn("rez_avatar/request from " + m_scene.Name + " for child agent " + agent.Name + " failed: " + map["message"].AsString()); return(false); } }