diff --git a/OpenSim.RegionServer/OpenSimMain.cs b/OpenSim.RegionServer/OpenSimMain.cs index 038c7e62b3..7f2032d5d2 100644 --- a/OpenSim.RegionServer/OpenSimMain.cs +++ b/OpenSim.RegionServer/OpenSimMain.cs @@ -222,6 +222,26 @@ namespace OpenSim return new XmlRpcResponse(); }); + + httpServer.AddXmlRPCHandler("agent_crossing", + delegate(XmlRpcRequest request) + { + Hashtable requestData = (Hashtable)request.Params[0]; + AgentCircuitData agent_data = new AgentCircuitData(); + agent_data.firstname = (string)requestData["firstname"]; + agent_data.lastname = (string)requestData["lastname"]; + agent_data.circuitcode = Convert.ToUInt32(requestData["circuit_code"]); + agent_data.startpos = new LLVector3(Single.Parse((string)requestData["pos_x"]),Single.Parse((string)requestData["pos_y"]),Single.Parse((string)requestData["pos_z"])); + + if(((RemoteGridBase)gridServer).agentcircuits.ContainsKey((uint)agent_data.circuitcode)) { + ((RemoteGridBase)gridServer).agentcircuits[(uint)agent_data.circuitcode].firstname = agent_data.firstname; + ((RemoteGridBase)gridServer).agentcircuits[(uint)agent_data.circuitcode].lastname = agent_data.lastname; + ((RemoteGridBase)gridServer).agentcircuits[(uint)agent_data.circuitcode].startpos = agent_data.startpos; + } + + return new XmlRpcResponse(); + }); + httpServer.AddRestHandler("GET", "/simstatus/", delegate(string request, string path, string param) { diff --git a/OpenSim.RegionServer/SimClient.cs b/OpenSim.RegionServer/SimClient.cs index 083ca6886f..52074b5bfa 100644 --- a/OpenSim.RegionServer/SimClient.cs +++ b/OpenSim.RegionServer/SimClient.cs @@ -144,19 +144,19 @@ namespace OpenSim if(avatarpos.X<0) { neighbourx-=1; - newpos.X=0; + newpos.X=254; } if(avatarpos.X>255) { neighbourx+=1; - newpos.X=255; + newpos.X=1; } if(avatarpos.Y<0) { neighboury-=1; - newpos.Y=0; + newpos.Y=254; } if(avatarpos.Y>255) { neighbourx+=1; - newpos.Y=255; + newpos.Y=1; } OpenSim.Framework.Console.MainConsole.Instance.WriteLine("SimClient.cs:CrossSimBorder() - Crossing border to neighbouring sim at [" + neighbourx.ToString() + "," + neighboury.ToString() + "]"); @@ -166,6 +166,19 @@ namespace OpenSim XmlRpcResponse GridResp; foreach(Hashtable borderingSim in ((RemoteGridBase)m_gridServer).neighbours) { if(((string)borderingSim["region_locx"]).Equals(neighbourx.ToString()) && ((string)borderingSim["region_locy"]).Equals(neighboury.ToString())) { + SimParams = new Hashtable(); + SimParams["firstname"] = this.ClientAvatar.firstname; + SimParams["lastname"] = this.ClientAvatar.lastname; + SimParams["circuit_code"] = this.CircuitCode.ToString(); + SimParams["pos_x"] = newpos.X.ToString(); + SimParams["pos_y"] = newpos.Y.ToString(); + SimParams["pos_z"] = newpos.Z.ToString(); + SendParams = new ArrayList(); + SendParams.Add(SimParams); + + GridReq = new XmlRpcRequest("agent_crossing", SendParams); + GridResp = GridReq.Send("http://" + borderingSim["sim_ip"] + ":" + borderingSim["sim_port"], 3000); + CrossedRegionPacket NewSimPack = new CrossedRegionPacket(); NewSimPack.AgentData = new CrossedRegionPacket.AgentDataBlock(); NewSimPack.AgentData.AgentID=this.AgentID; @@ -196,6 +209,8 @@ namespace OpenSim OpenSim.Framework.Console.MainConsole.Instance.WriteLine("SimClient.cs:UpgradeClient() - upgrading child to full agent"); this.m_child=false; this.m_world.RemoveViewerAgent(this); + this.startpos=((RemoteGridBase)m_gridServer).agentcircuits[CircuitCode].startpos; + ((RemoteGridBase)m_gridServer).agentcircuits[CircuitCode].child=false; this.InitNewClient(); } diff --git a/OpenSim.RegionServer/SimComms.cs b/OpenSim.RegionServer/SimComms.cs new file mode 100644 index 0000000000..a2b936bd90 --- /dev/null +++ b/OpenSim.RegionServer/SimComms.cs @@ -0,0 +1,437 @@ +/* +Copyright (c) OpenSim project, http://osgrid.org/ +* +* 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 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 ``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 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 Nwc.XmlRpc; +using System.Net; +using System.Net.Sockets; +using System.IO; +using System.Threading; +using System.Timers; +using OpenSim.Framework.Interfaces; +using OpenSim.Framework.Assets; +using OpenSim.Framework.Inventory; +using OpenSim.Framework.Utilities; +using OpenSim.world; +using OpenSim.Assets; + +namespace OpenSim +{ + public delegate bool PacketMethod(SimComms remoteSim, Packet packet); + + /// + /// Handles sim<->sim communications + /// Constructor takes a single Packet and authenticates everything + /// + public class SimComms + { + public LLUUID SimUUID; + public bool m_child; + public uint CircuitCode; + private UseCircuitCodePacket cirpack; + public Thread ClientThread; + public EndPoint simEP; + private BlockingQueue PacketQueue; + private Dictionary PendingAcks = new Dictionary(); + private Dictionary NeedAck = new Dictionary(); + private System.Timers.Timer AckTimer; + private uint Sequence = 0; + private object SequenceLock = new object(); + private const int MAX_APPENDED_ACKS = 10; + private const int RESEND_TIMEOUT = 4000; + private const int MAX_SEQUENCE = 0xFFFFFF; + private bool debug = false; + private World m_world; + private Dictionary m_simThreads; + private IGridServer m_gridServer; + private IUserServer m_userServer = null; + private OpenSimNetworkHandler m_application; + //private bool m_sandboxMode; // I can think of no reason why we'd ever have this set in sandbox mode + + protected static Dictionary PacketHandlers = new Dictionary(); //Global/static handlers for all sims + + protected Dictionary m_packetHandlers = new Dictionary(); //local handlers for this instance + + public IUserServer UserServer + { + set + { + this.m_userServer = value; + } + } + + public SimComms(EndPoint remoteEP, UseCircuitCodePacket initialcirpack, World world, Dictionary simThreads, IGridServer gridServer, OpenSimNetworkHandler application) + { + m_world = world; + m_clientThreads = clientThreads; + m_gridServer = gridServer; + m_application = application; + + OpenSim.Framework.Console.MainConsole.Instance.WriteLine("SimComms.cs - Started up new thread to handle incoming request"); + cirpack = initialcirpack; + simEP = remoteEP; + + PacketQueue = new BlockingQueue(); + + AckTimer = new System.Timers.Timer(500); + AckTimer.Elapsed += new ElapsedEventHandler(AckTimer_Elapsed); + AckTimer.Start(); + + this.RegisterLocalPacketHandlers(); + + ClientThread = new Thread(new ThreadStart(AuthUser)); + ClientThread.IsBackground = true; + ClientThread.Start(); + } + + protected virtual void RegisterLocalPacketHandlers() + { +/* this.AddLocalPacketHandler(PacketType.LogoutRequest, this.Logout); + this.AddLocalPacketHandler(PacketType.AgentCachedTexture, this.AgentTextureCached); + this.AddLocalPacketHandler(PacketType.MultipleObjectUpdate, this.MultipleObjUpdate);*/ + } + + public static bool AddPacketHandler(PacketType packetType, PacketMethod handler) + { + bool result = false; + lock (PacketHandlers) + { + if (!PacketHandlers.ContainsKey(packetType)) + { + PacketHandlers.Add(packetType, handler); + result = true; + } + } + return result; + } + + public bool AddLocalPacketHandler(PacketType packetType, PacketMethod handler) + { + bool result = false; + lock (m_packetHandlers) + { + if (!m_packetHandlers.ContainsKey(packetType)) + { + m_packetHandlers.Add(packetType, handler); + result = true; + } + } + return result; + } + + protected virtual bool ProcessPacketMethod(Packet packet) + { + bool result = false; + bool found = false; + PacketMethod method; + if (m_packetHandlers.TryGetValue(packet.Type, out method)) + { + //there is a local handler for this packet type + result = method(this, packet); + } + else + { + //there is not a local handler so see if there is a Global handler + lock (PacketHandlers) + { + found = PacketHandlers.TryGetValue(packet.Type, out method); + } + if (found) + { + result = method(this, packet); + } + } + return result; + } + + private 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 virtual void ProcessInPacket(Packet Pack) + { + ack_pack(Pack); + + if (this.ProcessPacketMethod(Pack)) + { + //there is a handler registered that handled this packet type + return; + } + else + { + System.Text.Encoding _enc = System.Text.Encoding.ASCII; + + switch (Pack.Type) + { + } + } + } + + private 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("Resending " + packet.Type.ToString() + " packet, " + + (now - packet.TickCount) + "ms have passed"); + + packet.Header.Resent = true; + OutPacket(packet); + } + } + } + } + + private 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("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(); + } + } + } + + private void AckTimer_Elapsed(object sender, ElapsedEventArgs ea) + { + SendAcks(); + ResendUnacked(); + } + + 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)) + { + NeedAck.Add(Pack.Header.Sequence, Pack); + } + 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_application.SendPacketTo(ZeroOutBuffer, packetsize, SocketFlags.None, CircuitCode);//userEP); + } + else + { + m_application.SendPacketTo(sendbuffer, sendbuffer.Length, SocketFlags.None, CircuitCode); //userEP); + } + } + catch (Exception) + { + OpenSim.Framework.Console.MainConsole.Instance.WriteLine("SimComms.cs:ProcessOutPacket() - WARNING: Socket exception occurred on connection " + simEP.ToString() + " - killing thread"); + ClientThread.Abort(); + } + + } + + 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); + } + + protected virtual void ClientLoop() + { + OpenSim.Framework.Console.MainConsole.Instance.WriteLine("SimComms.cs:ClientLoop() - Entered loop"); + while (true) + { + QueItem nextPacket = PacketQueue.Dequeue(); + if (nextPacket.Incoming) + { + //is a incoming packet + ProcessInPacket(nextPacket.Packet); + } + else + { + //is a out going packet + ProcessOutPacket(nextPacket.Packet); + } + } + } + + } +}