diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index cda8a1be49..6732d59adf 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -707,6 +707,12 @@ namespace OpenSim.Framework UUID AgentId { get; } + /// + /// The scene agent for this client. This will only be set if the client has an agent in a scene (i.e. if it + /// is connected). + /// + ISceneAgent SceneAgent { get; } + UUID SessionId { get; } UUID SecureSessionId { get; } diff --git a/OpenSim/Framework/IScene.cs b/OpenSim/Framework/IScene.cs index 7b0fe374c7..6919c48d91 100644 --- a/OpenSim/Framework/IScene.cs +++ b/OpenSim/Framework/IScene.cs @@ -68,12 +68,16 @@ namespace OpenSim.Framework event restart OnRestart; /// - /// Register the new client with the scene. The client starts off as a child agent - the later agent crossing - /// will promote it to a root agent. + /// Add a new client and create a presence for it. All clients except initial login clients will starts off as a child agent + /// - the later agent crossing will promote it to a root agent. /// /// /// The type of agent to add. - void AddNewClient(IClientAPI client, PresenceType type); + /// + /// The scene agent if the new client was added. + /// Null if the required scene agent already existed or no scene agent was added because the required client circuit doesn't exist. + /// + ISceneAgent AddNewClient(IClientAPI client, PresenceType type); /// /// Remove the given client from the scene. diff --git a/OpenSim/Framework/ISceneAgent.cs b/OpenSim/Framework/ISceneAgent.cs new file mode 100644 index 0000000000..69e91edfa7 --- /dev/null +++ b/OpenSim/Framework/ISceneAgent.cs @@ -0,0 +1,68 @@ +/* + * 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 OpenSimulator 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; + +namespace OpenSim.Framework +{ + /// + /// An agent in the scene. + /// + /// + /// Interface is a work in progress. Please feel free to add other required properties and methods. + /// + public interface ISceneAgent : ISceneEntity + { + /// + /// The client controlling this presence + /// + IClientAPI ControllingClient { get; } + + /// + /// What type of presence is this? User, NPC, etc. + /// + PresenceType PresenceType { get; } + + /// + /// Avatar appearance data. + /// + /// + // Because appearance setting is in a module, we actually need + // to give it access to our appearance directly, otherwise we + // get a synchronization issue. + /// + AvatarAppearance Appearance { get; set; } + + /// + /// Send initial scene data to the client controlling this agent + /// + /// + /// This includes scene object data and the appearance data of other avatars. + /// + void SendInitialDataToMe(); + } +} \ No newline at end of file diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index effa8d05eb..af68de60c8 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -390,6 +390,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } public UUID AgentId { get { return m_agentId; } } + public ISceneAgent SceneAgent { get; private set; } public UUID ActiveGroupId { get { return m_activeGroupID; } } public string ActiveGroupName { get { return m_activeGroupName; } } public ulong ActiveGroupPowers { get { return m_activeGroupPowers; } } @@ -531,6 +532,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Remove ourselves from the scene m_scene.RemoveClient(AgentId, true); + SceneAgent = null; // We can't reach into other scenes and close the connection // We need to do this over grid communications @@ -710,7 +712,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP public virtual void Start() { - m_scene.AddNewClient(this, PresenceType.User); + SceneAgent = m_scene.AddNewClient(this, PresenceType.User); RefreshGroupMembership(); } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index ae8251ae2e..b04fe9f546 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -894,11 +894,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP IPEndPoint remoteEndPoint = (IPEndPoint)buffer.RemoteEndPoint; // Begin the process of adding the client to the simulator - AddNewClient((UseCircuitCodePacket)packet, remoteEndPoint); + IClientAPI client = AddNewClient((UseCircuitCodePacket)packet, remoteEndPoint); - // Send ack + // Send ack straight away to let the viewer know that the connection is active. SendAckImmediate(remoteEndPoint, packet.Header.Sequence); + // FIXME: Nasty - this is the only way we currently know if Scene.AddNewClient() failed to find a + // circuit and bombed out early. That check might be pointless since authorization is established + // up here. + if (client != null && client.SceneAgent != null) + client.SceneAgent.SendInitialDataToMe(); + // m_log.DebugFormat( // "[LLUDPSERVER]: Handling UseCircuitCode request from {0} took {1}ms", // buffer.RemoteEndPoint, (DateTime.Now - startTime).Milliseconds); @@ -933,7 +939,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP return sessionInfo.Authorised; } - private void AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint) + /// + /// Add a new client. + /// + /// + /// + /// + /// The client that was added or null if the client failed authorization or already existed. + /// + private IClientAPI AddNewClient(UseCircuitCodePacket useCircuitCode, IPEndPoint remoteEndPoint) { UUID agentID = useCircuitCode.CircuitCode.ID; UUID sessionID = useCircuitCode.CircuitCode.SessionID; @@ -942,7 +956,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP AuthenticateResponse sessionInfo; if (IsClientAuthorized(useCircuitCode, out sessionInfo)) { - AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); + return AddClient(circuitCode, agentID, sessionID, remoteEndPoint, sessionInfo); } else { @@ -950,38 +964,44 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.WarnFormat( "[LLUDPSERVER]: Connection request for client {0} connecting with unnotified circuit code {1} from {2}", useCircuitCode.CircuitCode.ID, useCircuitCode.CircuitCode.Code, remoteEndPoint); + + return null; } } - protected virtual void AddClient(uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) + /// + /// Add a client. + /// + /// + /// + /// + /// + /// + /// The client if it was added. Null if the client already existed. + protected virtual IClientAPI AddClient( + uint circuitCode, UUID agentID, UUID sessionID, IPEndPoint remoteEndPoint, AuthenticateResponse sessionInfo) { + IClientAPI client = null; + // In priciple there shouldn't be more than one thread here, ever. // But in case that happens, we need to synchronize this piece of code // because it's too important lock (this) { - IClientAPI existingClient; - - if (!m_scene.TryGetClient(agentID, out existingClient)) + if (!m_scene.TryGetClient(agentID, out client)) { - // Create the LLUDPClient LLUDPClient udpClient = new LLUDPClient(this, ThrottleRates, m_throttle, circuitCode, agentID, remoteEndPoint, m_defaultRTO, m_maxRTO); - // Create the LLClientView - LLClientView client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); + + client = new LLClientView(remoteEndPoint, m_scene, this, udpClient, sessionInfo, agentID, sessionID, circuitCode); client.OnLogout += LogoutHandler; - client.DisableFacelights = m_disableFacelights; + ((LLClientView)client).DisableFacelights = m_disableFacelights; - // Start the IClientAPI client.Start(); - - } - else - { - m_log.WarnFormat("[LLUDPSERVER]: Ignoring a repeated UseCircuitCode from {0} at {1} for circuit {2}", - existingClient.AgentId, remoteEndPoint, circuitCode); } } + + return client; } private void RemoveClient(LLUDPClient udpClient) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs index 737c6548a6..fb94355df6 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/MockScene.cs @@ -53,9 +53,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests public override void Update() {} public override void LoadWorldMap() {} - public override void AddNewClient(IClientAPI client, PresenceType type) + public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) { client.OnObjectName += RecordObjectNameCall; + + // FIXME + return null; } public override void RemoveClient(UUID agentID, bool someReason) {} diff --git a/OpenSim/Region/Framework/Interfaces/IScenePresence.cs b/OpenSim/Region/Framework/Interfaces/IScenePresence.cs index ff39283b25..5e43843faf 100644 --- a/OpenSim/Region/Framework/Interfaces/IScenePresence.cs +++ b/OpenSim/Region/Framework/Interfaces/IScenePresence.cs @@ -38,28 +38,8 @@ namespace OpenSim.Region.Framework.Interfaces /// /// Interface is a work in progress. Please feel free to add other required properties and methods. /// - public interface IScenePresence : ISceneEntity + public interface IScenePresence : ISceneAgent { - /// - /// The client controlling this presence - /// - IClientAPI ControllingClient { get; } - - /// - /// What type of presence is this? User, NPC, etc. - /// - PresenceType PresenceType { get; } - - /// - /// Avatar appearance data. - /// - /// - // Because appearance setting is in a module, we actually need - // to give it access to our appearance directly, otherwise we - // get a synchronization issue. - /// - AvatarAppearance Appearance { get; set; } - /// /// The AttachmentsModule synchronizes on this to avoid race conditions between commands to add and remove attachments. /// diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index f9ae39caa9..a51fca273d 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -2610,17 +2610,17 @@ namespace OpenSim.Region.Framework.Scenes #region Add/Remove Avatar Methods /// - /// Add a new client and create a child agent for it. + /// Add a new client and create a child scene presence for it. /// /// /// The type of agent to add. - public override void AddNewClient(IClientAPI client, PresenceType type) + public override ISceneAgent AddNewClient(IClientAPI client, PresenceType type) { AgentCircuitData aCircuit = m_authenticateHandler.GetAgentCircuitData(client.CircuitCode); bool vialogin = false; if (aCircuit == null) // no good, didn't pass NewUserConnection successfully - return; + return null; vialogin = (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaHGLogin) != 0 || (aCircuit.teleportFlags & (uint)Constants.TeleportFlags.ViaLogin) != 0; @@ -2628,60 +2628,61 @@ namespace OpenSim.Region.Framework.Scenes CheckHeartbeat(); ScenePresence presence; - if (GetScenePresence(client.AgentId) == null) // ensure there is no SP here + ScenePresence sp = GetScenePresence(client.AgentId); + + // XXX: Not sure how good it is to add a new client if a scene presence already exists. Possibly this + // could occur if a viewer crashes and relogs before the old client is kicked out. But this could cause + // other problems, and possible the code calling AddNewClient() should ensure that no client is already + // connected. + if (sp == null) { - m_log.Debug("[SCENE]: Adding new agent " + client.Name + " to scene " + RegionInfo.RegionName); + m_log.Debug("[SCENE]: Adding new child scene presence " + client.Name + " to scene " + RegionInfo.RegionName); m_clientManager.Add(client); SubscribeToClientEvents(client); - ScenePresence sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type); + sp = m_sceneGraph.CreateAndAddChildScenePresence(client, aCircuit.Appearance, type); m_eventManager.TriggerOnNewPresence(sp); sp.TeleportFlags = (TeleportFlags)aCircuit.teleportFlags; - // HERE!!! Do the initial attachments right here - // first agent upon login is a root agent by design. - // All other AddNewClient calls find aCircuit.child to be true + // The first agent upon login is a root agent by design. + // For this agent we will have to rez the attachments. + // All other AddNewClient calls find aCircuit.child to be true. if (aCircuit.child == false) { + // We have to set SP to be a root agent here so that SP.MakeRootAgent() will later not try to + // start the scripts again (since this is done in RezAttachments()). + // XXX: This is convoluted. sp.IsChildAgent = false; if (AttachmentsModule != null) Util.FireAndForget(delegate(object o) { AttachmentsModule.RezAttachments(sp); }); } } - - ScenePresence createdSp = GetScenePresence(client.AgentId); - if (createdSp != null) + else { - m_LastLogin = Util.EnvironmentTickCount(); - - // Cache the user's name - CacheUserName(createdSp, aCircuit); - - EventManager.TriggerOnNewClient(client); - if (vialogin) - { - EventManager.TriggerOnClientLogin(client); - - // Send initial parcel data - Vector3 pos = createdSp.AbsolutePosition; - ILandObject land = LandChannel.GetLandObject(pos.X, pos.Y); - land.SendLandUpdateToClient(client); - } + m_log.WarnFormat( + "[SCENE]: Already found {0} scene presence for {1} in {2} when asked to add new scene presence", + sp.IsChildAgent ? "child" : "root", sp.Name, RegionInfo.RegionName); } - // Send all scene object to the new client - Util.FireAndForget(delegate + m_LastLogin = Util.EnvironmentTickCount(); + + // Cache the user's name + CacheUserName(sp, aCircuit); + + EventManager.TriggerOnNewClient(client); + if (vialogin) { - EntityBase[] entities = Entities.GetEntities(); - foreach(EntityBase e in entities) - { - if (e != null && e is SceneObjectGroup) - ((SceneObjectGroup)e).SendFullUpdateToClient(client); - } - }); + EventManager.TriggerOnClientLogin(client); + // Send initial parcel data + Vector3 pos = sp.AbsolutePosition; + ILandObject land = LandChannel.GetLandObject(pos.X, pos.Y); + land.SendLandUpdateToClient(client); + } + + return sp; } /// diff --git a/OpenSim/Region/Framework/Scenes/SceneBase.cs b/OpenSim/Region/Framework/Scenes/SceneBase.cs index 29ab071d21..84da700a08 100644 --- a/OpenSim/Region/Framework/Scenes/SceneBase.cs +++ b/OpenSim/Region/Framework/Scenes/SceneBase.cs @@ -177,7 +177,7 @@ namespace OpenSim.Region.Framework.Scenes #region Add/Remove Agent/Avatar - public abstract void AddNewClient(IClientAPI client, PresenceType type); + public abstract ISceneAgent AddNewClient(IClientAPI client, PresenceType type); public abstract void RemoveClient(UUID agentID, bool closeChildAgents); public bool TryGetScenePresence(UUID agentID, out object scenePresence) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index 3c97852ee6..2748a75800 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -794,15 +794,6 @@ namespace OpenSim.Region.Framework.Scenes AdjustKnownSeeds(); - // we created a new ScenePresence (a new child agent) in a fresh region. - // Request info about all the (root) agents in this region - // Note: This won't send data *to* other clients in that region (children don't send) - -// MIC: This gets called again in CompleteMovement - // SendInitialFullUpdateToAllClients(); - SendOtherAgentsAvatarDataToMe(); - SendOtherAgentsAppearanceToMe(); - RegisterToEvents(); SetDirectionVectors(); @@ -1207,9 +1198,9 @@ namespace OpenSim.Region.Framework.Scenes { // DateTime startTime = DateTime.Now; - m_log.DebugFormat( - "[SCENE PRESENCE]: Completing movement of {0} into region {1}", - client.Name, Scene.RegionInfo.RegionName); +// m_log.DebugFormat( +// "[SCENE PRESENCE]: Completing movement of {0} into region {1}", +// client.Name, Scene.RegionInfo.RegionName); Vector3 look = Velocity; if ((look.X == 0) && (look.Y == 0) && (look.Z == 0)) @@ -1241,7 +1232,7 @@ namespace OpenSim.Region.Framework.Scenes //m_log.DebugFormat("[SCENE PRESENCE] Completed movement"); ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look); - SendInitialData(); + ValidateAndSendAppearanceAndAgentData(); // Create child agents in neighbouring regions if (openChildAgents && !IsChildAgent) @@ -2531,11 +2522,31 @@ namespace OpenSim.Region.Framework.Scenes ControllingClient.SendCoarseLocationUpdate(avatarUUIDs, coarseLocations); } + public void SendInitialDataToMe() + { + // Send all scene object to the new client + Util.FireAndForget(delegate + { + // we created a new ScenePresence (a new child agent) in a fresh region. + // Request info about all the (root) agents in this region + // Note: This won't send data *to* other clients in that region (children don't send) + SendOtherAgentsAvatarDataToMe(); + SendOtherAgentsAppearanceToMe(); + + EntityBase[] entities = Scene.Entities.GetEntities(); + foreach(EntityBase e in entities) + { + if (e != null && e is SceneObjectGroup) + ((SceneObjectGroup)e).SendFullUpdateToClient(ControllingClient); + } + }); + } + /// /// Do everything required once a client completes its movement into a region and becomes /// a root agent. /// - private void SendInitialData() + private void ValidateAndSendAppearanceAndAgentData() { //m_log.DebugFormat("[SCENE PRESENCE] SendInitialData: {0} ({1})", Name, UUID); // Moved this into CompleteMovement to ensure that Appearance is initialized before diff --git a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs index 60d3c2d7cd..e62e30f6cb 100644 --- a/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs +++ b/OpenSim/Region/OptionalModules/Agent/InternetRelayClientView/Server/IRCClientView.cs @@ -55,6 +55,8 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server private UUID m_agentID = UUID.Random(); + public ISceneAgent SceneAgent { get; private set; } + private string m_username; private string m_nick; @@ -547,6 +549,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server m_connected = false; m_client.Close(); + SceneAgent = null; } public UUID SessionId @@ -897,12 +900,11 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server public void Start() { - Scene.AddNewClient(this, PresenceType.User); + SceneAgent = m_scene.AddNewClient(this, PresenceType.User); // Mimicking LLClientView which gets always set appearance from client. - Scene scene = (Scene)Scene; AvatarAppearance appearance; - scene.GetAvatarAppearance(this, out appearance); + m_scene.GetAvatarAppearance(this, out appearance); OnSetAppearance(this, appearance.Texture, (byte[])appearance.VisualParams.Clone()); } diff --git a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs index 2b60cc3afc..1b31663d7d 100644 --- a/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs +++ b/OpenSim/Region/OptionalModules/World/NPC/NPCAvatar.cs @@ -43,7 +43,6 @@ namespace OpenSim.Region.OptionalModules.World.NPC private readonly UUID m_uuid = UUID.Random(); private readonly Scene m_scene; - public NPCAvatar(string firstname, string lastname, Vector3 position, Scene scene) { m_firstname = firstname; @@ -57,6 +56,8 @@ namespace OpenSim.Region.OptionalModules.World.NPC get { return m_scene; } } + public ISceneAgent SceneAgent { get { throw new NotImplementedException(); } } + public void Say(string message) { SendOnChatFromClient(message, ChatTypeEnum.Say); @@ -845,6 +846,8 @@ namespace OpenSim.Region.OptionalModules.World.NPC public void Start() { + // We never start the client, so always fail. + throw new NotImplementedException(); } public void Stop() diff --git a/OpenSim/Tests/Common/Mock/TestClient.cs b/OpenSim/Tests/Common/Mock/TestClient.cs index 1e74451605..2b7895b33d 100644 --- a/OpenSim/Tests/Common/Mock/TestClient.cs +++ b/OpenSim/Tests/Common/Mock/TestClient.cs @@ -54,7 +54,7 @@ namespace OpenSim.Tests.Common.Mock public Scene TeleportTargetScene; private TestClient TeleportSceneClient; - private IScene m_scene; + private Scene m_scene; // Properties so that we can get at received data for test purposes public List ReceivedOfflineNotifications { get; private set; } @@ -325,6 +325,8 @@ namespace OpenSim.Tests.Common.Mock /// private UUID m_agentId; + public ISceneAgent SceneAgent { get { throw new NotImplementedException(); } } + /// /// The last caps seed url that this client was given. /// @@ -438,7 +440,7 @@ namespace OpenSim.Tests.Common.Mock /// /// /// - public TestClient(AgentCircuitData agentData, IScene scene) + public TestClient(AgentCircuitData agentData, Scene scene) { m_agentId = agentData.AgentID; m_firstName = agentData.firstname; @@ -904,6 +906,7 @@ namespace OpenSim.Tests.Common.Mock public void Start() { + throw new NotImplementedException(); } public void Stop()