diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs index a956c76f4e..b1e774154e 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncAvatar.cs @@ -406,10 +406,8 @@ namespace OpenSim.Region.Examples.RegionSyncModule return 0; } - public virtual int NextAnimationSequenceNumber - { - get { return 1; } - } + private int m_animationSequenceNumber = 1; + public int NextAnimationSequenceNumber { get { return m_animationSequenceNumber++; } } public IScene Scene { @@ -763,6 +761,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule private void Update() { + /* frame++; if (frame > 20) { @@ -798,7 +797,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule if (OnChatFromClient != null) { OSChatMessage args = new OSChatMessage(); - args.Message = "Hey You! Get out of my Home. This is my Region"; + args.Message = "";//Hey You! Get out of my Home. This is my Region"; args.Channel = 0; args.From = FirstName + " " + LastName; args.Scene = m_scene; @@ -813,6 +812,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule count++; } + * */ } public bool AddMoney(int debit) diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs index 2f12e22efe..24f8e37152 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs @@ -6,6 +6,7 @@ using System.Text; using System.Collections.Generic; using System.Threading; using OpenMetaverse; +using OpenMetaverse.Packets; using OpenMetaverse.StructuredData; using OpenSim.Framework; using OpenSim.Framework.Client; @@ -40,6 +41,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule private Scene m_scene; // The avatars added to this client manager for clients on other client managers + object m_syncRoot = new object(); Dictionary m_remoteAvatars = new Dictionary(); Dictionary m_localAvatars = new Dictionary(); @@ -62,7 +64,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule public RegionSyncClient(Scene scene, string addr, int port) { m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - m_log.WarnFormat("{0} Constructed", LogHeader); + //m_log.WarnFormat("{0} Constructed", LogHeader); m_scene = scene; m_addr = IPAddress.Parse(addr); m_port = port; @@ -87,15 +89,14 @@ namespace OpenSim.Region.Examples.RegionSyncModule m_rcvLoop.Name = "RegionSyncClient ReceiveLoop"; m_log.WarnFormat("{0} Starting {1} thread", LogHeader, m_rcvLoop.Name); m_rcvLoop.Start(); - m_log.WarnFormat("{0} Started", LogHeader); + //m_log.WarnFormat("{0} Started", LogHeader); DoInitialSync(); } // Disconnect from the RegionSyncServer and close the RegionSyncClient public void Stop() { - // Can't abort the TCP connection until we let the remote scene know we are going away. - // Or, the remote scene will remove the CM's avatars automatically when it disconnects + // The remote scene will remove our avatars automatically when we disconnect m_rcvLoop.Abort(); ShutdownClient(); } @@ -104,24 +105,37 @@ namespace OpenSim.Region.Examples.RegionSyncModule { m_log.WarnFormat("{0} Disconnected from RegionSyncServer. Shutting down.", LogHeader); - // Remove remote avatars from local scene - lock (m_remoteAvatars) + lock (m_syncRoot) { - foreach (UUID id in m_remoteAvatars.Keys) + // Remove remote avatars from local scene + try { - m_scene.RemoveClient(id); + List remoteAvatars = new List(m_remoteAvatars.Keys); + foreach (UUID id in remoteAvatars) + { + m_scene.RemoveClient(id); + } } - } - // Remove local avatars from remote scene - lock (m_localAvatars) - { - foreach (KeyValuePair kvp in m_localAvatars) + catch (Exception e) { - // Tell the remote scene to remove this client - RemoveLocalClient(kvp.Key, m_scene); - // Remove the agent update handler from the client - kvp.Value.OnAgentUpdateRaw -= HandleAgentUpdateRaw; - kvp.Value.OnSetAppearanceRaw -= HandleSetAppearanceRaw; + m_log.ErrorFormat("Caught exception while removing remote avatars on Shutdown: {0}", e.Message); + } + try + { + // Remove local avatars from remote scene + List> localAvatars = new List>(m_localAvatars); + foreach (KeyValuePair kvp in localAvatars) + { + // Tell the remote scene to remove this client + RemoveLocalClient(kvp.Key, m_scene); + // Remove the agent update handler from the client + kvp.Value.OnAgentUpdateRaw -= HandleAgentUpdateRaw; + kvp.Value.OnSetAppearanceRaw -= HandleSetAppearanceRaw; + } + } + catch (Exception e) + { + m_log.ErrorFormat("Caught exception while removing local avatars on Shutdown: {0}", e.Message); } } // Close the connection @@ -154,7 +168,9 @@ namespace OpenSim.Region.Examples.RegionSyncModule try { string message; - MsgHandlerStatus status = HandleMessage(msg, out message); + MsgHandlerStatus status; + lock(m_syncRoot) + status = HandleMessage(msg, out message); switch (status) { case MsgHandlerStatus.Success: @@ -218,7 +234,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule List ids = new List(); List locations = new List(); m_scene.GetCoarseLocations(out ids, out locations); - lock (m_localAvatars) + lock (m_syncRoot) { foreach (IClientAPI client in m_localAvatars.Values) { @@ -330,113 +346,226 @@ namespace OpenSim.Region.Examples.RegionSyncModule case RegionSyncMessage.MsgType.NewAvatar: { OSDMap data = DeserializeMessage(msg); - if (data != null) + if (data == null) { - UUID agentID = data["agentID"].AsUUID(); - string first = data["first"].AsString(); - string last = data["last"].AsString(); - Vector3 startPos = data["startPos"].AsVector3(); - - if (agentID != null && first != null && last != null && startPos != null) - { - if (m_remoteAvatars.ContainsKey(agentID)) - { - result = String.Format("Attempted to add duplicate avatar \"{0} {1}\" ({2})", first, last, agentID.ToString()); - return MsgHandlerStatus.Warning; - } - ScenePresence sp; - if (m_scene.TryGetScenePresence(agentID, out sp)) - { - result = String.Format("Confirmation of new avatar \"{0}\" ({1})", sp.Name, sp.UUID.ToString()); - HandlerDebug(msg, result); - return MsgHandlerStatus.Success; - } - RegionSyncAvatar av = new RegionSyncAvatar(m_scene, agentID, first, last, startPos); - m_remoteAvatars.Add(agentID, av); - m_scene.AddNewClient(av); - result = String.Format("Handled NewAvatar for UUID {0}", agentID); - return MsgHandlerStatus.Success; - } + result = "Could not deserialize JSON data."; + return MsgHandlerStatus.Error; } - result = "Could not deserialize JSON data."; - return MsgHandlerStatus.Error; + + UUID agentID = data["agentID"].AsUUID(); + string first = data["first"].AsString(); + string last = data["last"].AsString(); + Vector3 startPos = data["startPos"].AsVector3(); + + if (agentID == null || agentID == UUID.Zero || first == null || last == null || startPos == null) + { + result = "Missing or invalid JSON data."; + return MsgHandlerStatus.Error; + } + + if (m_remoteAvatars.ContainsKey(agentID)) + { + result = String.Format("Attempted to add duplicate avatar \"{0} {1}\" ({2})", first, last, agentID.ToString()); + return MsgHandlerStatus.Warning; + } + + ScenePresence sp; + if (m_scene.TryGetScenePresence(agentID, out sp)) + { + result = String.Format("Confirmation of new avatar \"{0}\" ({1})", sp.Name, sp.UUID.ToString()); + HandlerDebug(msg, result); + return MsgHandlerStatus.Success; + } + + RegionSyncAvatar av = new RegionSyncAvatar(m_scene, agentID, first, last, startPos); + m_remoteAvatars.Add(agentID, av); + m_scene.AddNewClient(av); + result = String.Format("Added new remote avatar \"{0}\" ({1})", first + " " + last, agentID); + HandlerDebug(msg, result); + return MsgHandlerStatus.Success; + } case RegionSyncMessage.MsgType.UpdatedAvatar: { + // Get the data from message and error check OSDMap data = DeserializeMessage(msg); - if (data != null) + if (data == null) { - UUID agentID = data["id"].AsUUID(); - Vector3 pos = data["pos"].AsVector3(); - Vector3 vel = data["vel"].AsVector3(); - Quaternion rot = data["rot"].AsQuaternion(); - bool flying = data["fly"].AsBoolean(); + result = "Could not deserialize JSON data."; + return MsgHandlerStatus.Error; + } - // We get the UUID of the avatar to be updated, find it in the scene - if (agentID != UUID.Zero) - { - ScenePresence presence; - if (m_scene.TryGetScenePresence(agentID, out presence)) - { - presence.AbsolutePosition = pos; - presence.Velocity = vel; - presence.Rotation = rot; - presence.PhysicsActor.Flying = flying; - List locals; - lock (m_localAvatars) - locals = new List(m_localAvatars.Values); - presence.SendTerseUpdateToClientList(locals); - result = String.Format("Avatar \"{0}\" ({1}) ({2}) updated (pos:{3}, vel:{4}, rot:{5}, fly:{6})", - presence.Name, presence.UUID.ToString(), presence.LocalId.ToString(), - presence.AbsolutePosition.ToString(), presence.Velocity.ToString(), - presence.Rotation.ToString(), presence.PhysicsActor.Flying ? "Y" : "N"); - return MsgHandlerStatus.Success; - } - } + // Get the parameters from data and error check + UUID agentID = data["id"].AsUUID(); + Vector3 pos = data["pos"].AsVector3(); + Vector3 vel = data["vel"].AsVector3(); + Quaternion rot = data["rot"].AsQuaternion(); + bool flying = data["fly"].AsBoolean(); + uint flags = data["flags"].AsUInteger(); + if (agentID == null || agentID == UUID.Zero ) + { + result = "Missing or invalid JSON data."; + return MsgHandlerStatus.Error; + } + + // Find the presence in the scene and error check + ScenePresence presence; + m_scene.TryGetScenePresence(agentID, out presence); + if (presence == null) + { result = String.Format("agentID {0} not found.", agentID.ToString()); return MsgHandlerStatus.Warning; } - result = "Could not deserialize JSON data."; - return MsgHandlerStatus.Error; + + // Update the scene presence from parameters + /* + bool updateAnimations = ((presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_FLY ) || + (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_POS ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_POS ) || + (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG ) || + (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_POS ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_POS ) || + (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG ) || + (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT ) || + (presence.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT ) != (flags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT )); + * */ + presence.AgentControlFlags = flags; + presence.AbsolutePosition = pos; + presence.Velocity = vel; + presence.Rotation = rot; + bool updateAnimations = + presence.PhysicsActor.Flying = flying; + List locals; + lock (m_syncRoot) + locals = new List(m_localAvatars.Values); + presence.SendTerseUpdateToClientList(locals); + if(updateAnimations) + presence.Animator.UpdateMovementAnimations(); + /* + * result = String.Format("Avatar \"{0}\" ({1}) ({2}) updated (pos:{3}, vel:{4}, rot:{5}, fly:{6})", + presence.Name, presence.UUID.ToString(), presence.LocalId.ToString(), + presence.AbsolutePosition.ToString(), presence.Velocity.ToString(), + presence.Rotation.ToString(), presence.PhysicsActor.Flying ? "Y" : "N"); + * */ + result = ""; // For performance, skip creating that long string unless debugging + return MsgHandlerStatus.Success; } case RegionSyncMessage.MsgType.RemovedAvatar: { + // Get the data from message and error check OSDMap data = DeserializeMessage(msg); - if (data != null) + if (data == null) { - UUID agentID = data["agentID"].AsUUID(); - - // We get the UUID of the avatar to be removed, find it in the scene - if (agentID != UUID.Zero) - { - ScenePresence presence; - // If this is a synced remote avatar and it's in the scene, then remove it - if (m_remoteAvatars.ContainsKey(agentID)) - { - if (m_scene.TryGetScenePresence(agentID, out presence)) - { - m_scene.RemoveClient(agentID); - result = String.Format("Avatar \"{0}\" removed from scene", presence.Name); - return MsgHandlerStatus.Success; - } - else - { - result = String.Format("agentID {0} not found.", agentID.ToString()); - return MsgHandlerStatus.Warning; - } - } - else - { - result = String.Format("agentID {0} is not a remote avatar.", agentID.ToString()); - return MsgHandlerStatus.Warning; - } - } - result = String.Format("agentID {0} was UUID.Zero!", agentID.ToString()); + result = "Could not deserialize JSON data."; return MsgHandlerStatus.Error; } - result = "Could not deserialize JSON data."; + + // Get the agentID from data and error check + UUID agentID = data["agentID"].AsUUID(); + if (agentID == UUID.Zero) + { + result = "Missing or invalid JSON data."; + return MsgHandlerStatus.Error; + } + + // Find the presence in the scene and error check + ScenePresence presence; + m_scene.TryGetScenePresence(agentID, out presence); + if(presence == null) + { + result = String.Format("agentID {0} not found.", agentID.ToString()); + return MsgHandlerStatus.Success; + } + + // Remove the presence from the scene and if it's remote, then from the remote list too + m_scene.RemoveClient(agentID); + if (m_remoteAvatars.ContainsKey(agentID)) + { + result = String.Format("Remote avatar \"{0}\" removed from scene", presence.Name); + HandlerDebug(msg, result); + return MsgHandlerStatus.Success; + } + else if(m_localAvatars.ContainsKey(agentID)) + { + result = String.Format("Received confirmation of removed avatar \"{0}\" ({1})", presence.Name, presence.UUID.ToString()); + HandlerDebug(msg, result); + return MsgHandlerStatus.Success; + } + result = String.Format("Avatar is not local OR remote but was found in scene: \"{0}\" ({1})", presence.Name, presence.UUID.ToString()); + HandlerDebug(msg, result); return MsgHandlerStatus.Error; } + case RegionSyncMessage.MsgType.ChatFromClient: + { + // Get the data from message and error check + OSDMap data = DeserializeMessage(msg); + if (data == null) + { + result = "Could not deserialize JSON data."; + return MsgHandlerStatus.Error; + } + OSChatMessage args = new OSChatMessage(); + args.Channel = data["channel"].AsInteger(); + args.Message = data["msg"].AsString(); + args.Position = data["pos"].AsVector3(); + args.From = data["name"].AsString(); + UUID id = data["id"].AsUUID(); + args.Scene = m_scene; + args.Type = ChatTypeEnum.Say; + ScenePresence sp; + m_scene.TryGetScenePresence(id, out sp); + if (sp != null) + { + args.Sender = sp.ControllingClient; + args.SenderUUID = id; + m_scene.EventManager.TriggerOnChatBroadcast(sp.ControllingClient, args); + } + + result = String.Format("Received chat from \"{0}\"", args.From); + HandlerDebug(msg, result); + return MsgHandlerStatus.Success; + } + case RegionSyncMessage.MsgType.AvatarAppearance: + { + // Get the data from message and error check + OSDMap data = DeserializeMessage(msg); + if (data == null) + { + result = "Could not deserialize JSON data."; + return MsgHandlerStatus.Error; + } + + // Get the parameters from data and error check + UUID agentID = data["id"].AsUUID(); + if (agentID == null || agentID == UUID.Zero) + { + result = "Missing or invalid JSON data."; + return MsgHandlerStatus.Error; + } + + // Find the presence in the scene + ScenePresence presence; + if (m_scene.TryGetScenePresence(agentID, out presence)) + { + string name = presence.Name; + Primitive.TextureEntry te = Primitive.TextureEntry.FromOSD(data["te"]); + byte[] vp = data["vp"].AsBinary(); + + byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; + for (int i = 0; i < BAKE_INDICES.Length; i++) + { + int j = BAKE_INDICES[i]; + Primitive.TextureEntryFace face = te.FaceTextures[j]; + if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) + if (m_scene.AssetService.Get(face.TextureID.ToString()) == null) + HandlerDebug(msg, "Missing baked texture " + face.TextureID + " (" + j + ") for avatar " + name); + } + + presence.SetAppearance(te, vp); + result = String.Format("Agent \"{0}\" ({1}) updated their appearance.", name, agentID); + return MsgHandlerStatus.Success; + } + result = String.Format("Agent {0} not found in the scene.", agentID); + return MsgHandlerStatus.Warning; + } /* case RegionSyncMessage.MsgType.UpdateObject: @@ -502,7 +631,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule return; } // Otherwise, it's a real local client connecting so track it locally - lock(m_localAvatars) + lock(m_syncRoot) m_localAvatars.Add(client.AgentId, client); m_log.WarnFormat("{0} New local client \"{1}\" ({2}) being added to remote scene.", LogHeader, client.Name, client.AgentId.ToString()); // Let the auth sim know that a new agent has connected @@ -522,12 +651,19 @@ namespace OpenSim.Region.Examples.RegionSyncModule void RemoveLocalClient(UUID clientID, Scene scene) { - m_log.WarnFormat("{0} Local client ({1}) being removed from remote scene.", LogHeader, clientID.ToString()); - m_localAvatars.Remove(clientID); - // Let the auth sim know that an agent has disconnected - OSDMap data = new OSDMap(1); - data["agentID"] = OSD.FromUUID(clientID); - Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentRemove, OSDParser.SerializeJsonString(data))); + lock (m_syncRoot) + { + // If the client to be removed is a local client, then send a message to the remote scene + if (m_localAvatars.ContainsKey(clientID)) + { + m_log.WarnFormat("{0} Local client ({1}) being removed from remote scene.", LogHeader, clientID.ToString()); + m_localAvatars.Remove(clientID); + // Let the auth sim know that an agent has disconnected + OSDMap data = new OSDMap(1); + data["agentID"] = OSD.FromUUID(clientID); + Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentRemove, OSDParser.SerializeJsonString(data))); + } + } } /// @@ -552,7 +688,29 @@ namespace OpenSim.Region.Examples.RegionSyncModule public void HandleChatFromClientRaw(object sender, byte[] chatData) { - Send(new RegionSyncMessage(RegionSyncMessage.MsgType.ChatFromClient, chatData)); + if (chatData != null && sender is IClientAPI) + { + IClientAPI client = (IClientAPI)sender; + ScenePresence presence; + m_scene.TryGetScenePresence(client.AgentId, out presence); + if(presence != null) + { + int len = 0; + ChatFromViewerPacket.ChatDataBlock cdb = new ChatFromViewerPacket.ChatDataBlock(); + cdb.FromBytes(chatData, ref len); + if (cdb.Message.Length == 0) + return; + OSDMap data = new OSDMap(5); + data["channel"] = OSD.FromInteger(cdb.Channel); + data["msg"] = OSD.FromString(Utils.BytesToString(cdb.Message)); + data["pos"] = OSD.FromVector3(presence.AbsolutePosition); + data["name"] = OSD.FromString(presence.Name); + data["id"] = OSD.FromUUID(presence.UUID); + + Send(new RegionSyncMessage(RegionSyncMessage.MsgType.ChatFromClient, OSDParser.SerializeJsonString(data))); + //m_log.WarnFormat("Forwarding chat message from local client \"{0}\" to remote scene", presence.Name); + } + } } #endregion diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs index 0b97a4fade..56af895b3b 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientModule.cs @@ -73,7 +73,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule if (!m_active) return; - m_log.Warn("[REGION SYNC CLIENT MODULE] Post-Initialised"); + //m_log.Warn("[REGION SYNC CLIENT MODULE] Post-Initialised"); } public void Close() @@ -202,7 +202,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule m_log.WarnFormat("[REGION SYNC CLIENT MODULE] Already synchronizing to {0}", m_client.GetServerAddressAndPort()); return; } - m_log.Warn("[REGION SYNC CLIENT MODULE] Starting synchronization"); + //m_log.Warn("[REGION SYNC CLIENT MODULE] Starting synchronization"); m_log.Warn("[REGION SYNC CLIENT MODULE] Starting RegionSyncClient"); m_client = new RegionSyncClient(m_scene, m_serveraddr, m_serverport); diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs index 9064dd74e6..9916ed61af 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClientView.cs @@ -79,28 +79,48 @@ namespace OpenSim.Region.Examples.RegionSyncModule m_port = ((IPEndPoint)client.Client.RemoteEndPoint).Port; m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - m_log.WarnFormat("{0} Constructed", LogHeader); + //m_log.WarnFormat("{0} Constructed", LogHeader); // Create a thread for the receive loop m_receive_loop = new Thread(new ThreadStart(delegate() { ReceiveLoop(); })); m_receive_loop.Name = Description; - m_log.WarnFormat("{0} Started thread: {1}", LogHeader, m_receive_loop.Name); + //m_log.WarnFormat("{0} Started thread: {1}", LogHeader, m_receive_loop.Name); m_receive_loop.Start(); } // Stop the RegionSyncClientView, disconnecting the RegionSyncClient public void Shutdown() { + m_scene.EventManager.OnChatFromClient -= EventManager_OnChatFromClient; // Abort ReceiveLoop Thread, close Socket and TcpClient m_receive_loop.Abort(); m_tcpclient.Client.Close(); m_tcpclient.Close(); + //Logout any synced avatars + lock (m_syncedAvatars) + { + foreach (UUID agentID in m_syncedAvatars.Keys) + { + ScenePresence presence; + if (m_scene.TryGetScenePresence(agentID, out presence)) + { + string name = presence.Name; + m_scene.RemoveClient(agentID); + m_log.WarnFormat("{0} Agent \"{1}\" ({2}) was removed from scene.", LogHeader, name, agentID); + } + else + { + m_log.WarnFormat("{0} Agent {1} not found in the scene.", LogHeader, agentID); + } + } + } } // Listen for messages from a RegionSyncClient // *** This is the main thread loop for each connected client private void ReceiveLoop() { + m_scene.EventManager.OnChatFromClient += new EventManager.ChatFromClientEvent(EventManager_OnChatFromClient); try { while (true) @@ -118,6 +138,18 @@ namespace OpenSim.Region.Examples.RegionSyncModule { m_log.WarnFormat("{0} RegionSyncClient has disconnected: {1}", LogHeader, e.Message); } + Shutdown(); + } + + void EventManager_OnChatFromClient(object sender, OSChatMessage chat) + { + OSDMap data = new OSDMap(5); + data["channel"] = OSD.FromInteger(chat.Channel); + data["msg"] = OSD.FromString(chat.Message); + data["pos"] = OSD.FromVector3(chat.Position); + data["name"] = OSD.FromString(chat.From); + data["id"] = OSD.FromUUID(chat.SenderUUID); + Send(new RegionSyncMessage(RegionSyncMessage.MsgType.ChatFromClient, OSDParser.SerializeJsonString(data))); } // Get a message from the RegionSyncClient @@ -255,77 +287,108 @@ namespace OpenSim.Region.Examples.RegionSyncModule } case RegionSyncMessage.MsgType.AgentRemove: { + // Get the data from message and error check OSDMap data = DeserializeMessage(msg); - if (data != null) + if (data == null) { - UUID agentID = data["agentID"].AsUUID(); - - if (agentID != null && agentID != UUID.Zero) - { - lock (m_syncedAvatars) - { - if (m_syncedAvatars.ContainsKey(agentID)) - { - m_syncedAvatars.Remove(agentID); - ScenePresence presence; - if (m_scene.TryGetScenePresence(agentID, out presence)) - { - string name = presence.Name; - m_scene.RemoveClient(agentID); - return HandlerSuccess(msg, String.Format("Agent \"{0}\" ({1}) was removed from scene.", name, agentID)); - } - else - { - return HandlerFailure(msg, String.Format("Agent {0} not found in the scene.", agentID)); - } - } - else - { - return HandlerFailure(msg, String.Format("Agent {0} not in the list of synced avatars.", agentID)); - } - } - } + return HandlerFailure(msg, "Could not deserialize JSON data."); } - return HandlerFailure(msg, "Could not deserialize JSON data."); - } - case RegionSyncMessage.MsgType.AvatarAppearance: - { - OSDMap data = DeserializeMessage(msg); - if (data != null) + // Get the parameters from data and error check + UUID agentID = data["agentID"].AsUUID(); + if (agentID == null || agentID == UUID.Zero) { - UUID agentID = data["id"].AsUUID(); + return HandlerFailure(msg, "Missing or invalid JSON data."); + } - if (agentID != null && agentID != UUID.Zero) + lock (m_syncedAvatars) + { + if (m_syncedAvatars.ContainsKey(agentID)) { + m_syncedAvatars.Remove(agentID); + // Find the presence in the scene ScenePresence presence; if (m_scene.TryGetScenePresence(agentID, out presence)) { string name = presence.Name; - Primitive.TextureEntry te = Primitive.TextureEntry.FromOSD(data["te"]); - byte[] vp = data["vp"].AsBinary(); - - byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; - for (int i = 0; i < BAKE_INDICES.Length; i++) - { - int j = BAKE_INDICES[i]; - Primitive.TextureEntryFace face = te.FaceTextures[j]; - if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) - if (m_scene.AssetService.Get(face.TextureID.ToString()) == null) - HandlerDebug(msg, "Missing baked texture " + face.TextureID + " (" + j + ") for avatar " + name); - } - - presence.SetAppearance(te, vp); - return HandlerDebug(msg, String.Format("Agent \"{0}\" ({1}) updated their appearance.", name, agentID)); + m_scene.RemoveClient(agentID); + return HandlerSuccess(msg, String.Format("Agent \"{0}\" ({1}) was removed from scene.", name, agentID)); } else { return HandlerFailure(msg, String.Format("Agent {0} not found in the scene.", agentID)); } } + else + { + return HandlerFailure(msg, String.Format("Agent {0} not in the list of synced avatars.", agentID)); + } + } + } + case RegionSyncMessage.MsgType.AvatarAppearance: + { + // Get the data from message and error check + OSDMap data = DeserializeMessage(msg); + if (data == null) + { + return HandlerFailure(msg, "Could not deserialize JSON data."); } - return HandlerFailure(msg, "Could not deserialize JSON data."); + // Get the parameters from data and error check + UUID agentID = data["id"].AsUUID(); + if (agentID == null || agentID == UUID.Zero) + { + return HandlerFailure(msg, "Missing or invalid JSON data."); + } + + // Find the presence in the scene + ScenePresence presence; + if (m_scene.TryGetScenePresence(agentID, out presence)) + { + string name = presence.Name; + Primitive.TextureEntry te = Primitive.TextureEntry.FromOSD(data["te"]); + byte[] vp = data["vp"].AsBinary(); + + byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 }; + for (int i = 0; i < BAKE_INDICES.Length; i++) + { + int j = BAKE_INDICES[i]; + Primitive.TextureEntryFace face = te.FaceTextures[j]; + if (face != null && face.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE) + if (m_scene.AssetService.Get(face.TextureID.ToString()) == null) + HandlerDebug(msg, "Missing baked texture " + face.TextureID + " (" + j + ") for avatar " + name); + } + + presence.SetAppearance(te, vp); + return HandlerDebug(msg, String.Format("Agent \"{0}\" ({1}) updated their appearance.", name, agentID)); + } + return HandlerFailure(msg, String.Format("Agent {0} not found in the scene.", agentID)); + } + case RegionSyncMessage.MsgType.ChatFromClient: + { + // Get the data from message and error check + OSDMap data = DeserializeMessage(msg); + if (data == null) + { + return HandlerFailure(msg, "Could not deserialize JSON data."); + } + OSChatMessage args = new OSChatMessage(); + args.Channel = data["channel"].AsInteger(); + args.Message = data["msg"].AsString(); + args.Position = data["pos"].AsVector3(); + args.From = data["name"].AsString(); + UUID id = data["id"].AsUUID(); + args.Scene = m_scene; + args.Type = ChatTypeEnum.Say; + ScenePresence sp; + m_scene.TryGetScenePresence(id, out sp); + if(sp != null) + { + args.Sender = sp.ControllingClient; + args.SenderUUID = id; + m_scene.EventManager.TriggerOnChatFromClient(sp.ControllingClient,args); + } + return HandlerSuccess(msg, String.Format("Received chat from \"{0}\"", args.From)); } default: { diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs index 7b3692599f..886b3619fe 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServer.cs @@ -98,7 +98,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule public RegionSyncServer(Scene scene, string addr, int port) { m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); - m_log.Warn("[REGION SYNC SERVER] Constructed"); + //m_log.Warn("[REGION SYNC SERVER] Constructed"); m_scene = scene; m_addr = IPAddress.Parse(addr); m_port = port; @@ -111,7 +111,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule m_listenerThread.Name = "RegionSyncServer Listener"; m_log.WarnFormat("[REGION SYNC SERVER] Starting {0} thread", m_listenerThread.Name); m_listenerThread.Start(); - m_log.Warn("[REGION SYNC SERVER] Started"); + //m_log.Warn("[REGION SYNC SERVER] Started"); } // Stop the server and disconnect all RegionSyncClients @@ -142,12 +142,10 @@ namespace OpenSim.Region.Examples.RegionSyncModule { // Start listening for clients m_listener.Start(); - m_log.WarnFormat("[REGION SYNC SERVER] Listening on port {0}", m_port.ToString()); - while (true) { // *** Move/Add TRY/CATCH to here, but we don't want to spin loop on the same error - m_log.Warn("[REGION SYNC SERVER] Waiting for a connection..."); + m_log.WarnFormat("[REGION SYNC SERVER] Listening for new connections on port {0}...", m_port.ToString()); TcpClient tcpclient = m_listener.AcceptTcpClient(); IPAddress addr = ((IPEndPoint)tcpclient.Client.RemoteEndPoint).Address; int port = ((IPEndPoint)tcpclient.Client.RemoteEndPoint).Port; diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServerModule.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServerModule.cs index d761f677cd..002f474b41 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServerModule.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncServerModule.cs @@ -67,7 +67,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; InstallInterfaces(); - m_log.Warn("[REGION SYNC SERVER MODULE] Initialised"); + //m_log.Warn("[REGION SYNC SERVER MODULE] Initialised"); } public void PostInitialise() @@ -93,7 +93,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule // Start the server and listen for RegionSyncClients m_server = new RegionSyncServer(m_scene, m_serveraddr, m_serverport); m_server.Start(); - m_log.Warn("[REGION SYNC SERVER MODULE] Post-Initialised"); + //m_log.Warn("[REGION SYNC SERVER MODULE] Post-Initialised"); } @@ -191,7 +191,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule { if (!presence.IsDeleted) { - OSDMap data = new OSDMap(5); + OSDMap data = new OSDMap(6); data["id"] = OSD.FromUUID(presence.UUID); // Do not include offset for appearance height. That will be handled by RegionSyncClient before sending to viewers if(presence.AbsolutePosition.IsFinite()) @@ -204,6 +204,7 @@ namespace OpenSim.Region.Examples.RegionSyncModule data["vel"] = OSD.FromVector3(Vector3.Zero); data["rot"] = OSD.FromQuaternion(presence.Rotation); data["fly"] = OSD.FromBoolean(presence.Flying); + data["flags"] = OSD.FromUInteger((uint)presence.AgentControlFlags); RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.UpdatedAvatar, OSDParser.SerializeJsonString(data)); m_server.Broadcast(rsm); } @@ -213,6 +214,18 @@ namespace OpenSim.Region.Examples.RegionSyncModule }); } + public void SendAppearance(UUID agentID, byte[] vp, Primitive.TextureEntry te) + { + if (te != null) + { + OSDMap data = new OSDMap(2); + data["id"] = OSDUUID.FromUUID(agentID); + data["vp"] = new OSDBinary(vp); + data["te"] = te.GetOSD(); + m_server.Broadcast(new RegionSyncMessage(RegionSyncMessage.MsgType.AvatarAppearance, OSDParser.SerializeJsonString(data))); + } + } + public void DeleteObject(ulong regionHandle, uint localID) { if (!Active || !Synced) diff --git a/OpenSim/Region/Framework/Interfaces/IRegionSyncServerModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionSyncServerModule.cs index 8a72ba9d6c..7d5d72eb69 100644 --- a/OpenSim/Region/Framework/Interfaces/IRegionSyncServerModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IRegionSyncServerModule.cs @@ -42,6 +42,7 @@ namespace OpenSim.Region.Framework.Interfaces void QueuePresenceForTerseUpdate(ScenePresence presence); void SendUpdates(); void DeleteObject(ulong regionHandle, uint localID); + void SendAppearance(UUID agentID, byte[] vp, Primitive.TextureEntry te); //void SendPartFullUpdate(SceneObjectPart part); diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs index fa3a5debe1..12defd6858 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.cs @@ -1440,7 +1440,8 @@ namespace OpenSim.Region.Framework.Scenes if (IsSyncedClient()) { ForEachScenePresence(delegate(ScenePresence sp) { sp.SendPrimUpdates(); }); - RegionSyncClientModule.SendCoarseLocations(); + if(m_frame % 20 == 0) + RegionSyncClientModule.SendCoarseLocations(); } else { @@ -3384,7 +3385,8 @@ namespace OpenSim.Region.Framework.Scenes /// public void NotifyMyCoarseLocationChange() { - ForEachScenePresence(delegate(ScenePresence presence) { presence.CoarseLocationChange(); }); + // REGION SYNC (Need a better plan for coarse locations) + //ForEachScenePresence(delegate(ScenePresence presence) { presence.CoarseLocationChange(); }); } #endregion diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index bed8aea8b0..d751036603 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -2257,7 +2257,7 @@ namespace OpenSim.Region.Framework.Scenes /// Rotate the avatar to the given rotation and apply a movement in the given relative vector /// /// The vector in which to move. This is relative to the rotation argument - /// The direction in which this avatar should now face. + /// The direction in which this avatar should now face. public void AddNewMovement(Vector3 vec, Quaternion rotation) { if (m_isChildAgent) @@ -2598,14 +2598,30 @@ namespace OpenSim.Region.Framework.Scenes { m_perfMonMS = Util.EnvironmentTickCount(); - m_scene.ForEachScenePresence(delegate(ScenePresence scenePresence) - { - if (scenePresence.UUID != UUID) - { - SendAppearanceToOtherAgent(scenePresence); - } - }); + // REGION SYNC + if(m_scene.IsSyncedServer()) + m_scene.RegionSyncServerModule.SendAppearance(UUID, Appearance.VisualParams, Appearance.Texture); + m_appearance.Owner = UUID; // Why does this need to be here? + m_scene.ForEachClient(delegate(IClientAPI client) + { + if(client.AgentId != ControllingClient.AgentId) + { + client.SendAppearance(m_appearance.Owner, m_appearance.VisualParams, m_appearance.Texture.GetBytes()); + } + }); + /* + + + m_scene.ForEachScenePresence(delegate(ScenePresence scenePresence) + { + if (scenePresence.UUID != UUID) + { + SendAppearanceToOtherAgent(scenePresence); + } + }); + } + * */ m_scene.StatsReporter.AddAgentTime(Util.EnvironmentTickCountSubtract(m_perfMonMS)); } diff --git a/bin/config-include/SimianGrid.ini b/bin/config-include/SimianGrid.ini index 41deb92db2..2e5045cf5b 100644 --- a/bin/config-include/SimianGrid.ini +++ b/bin/config-include/SimianGrid.ini @@ -1,67 +1,85 @@ -;; -;; Please don't change this file. -;; All optional settings are in GridCommon.ini.example, -;; which you can copy and change. -;; - -;; -;; In GridCommon.ini, these are the URLs you would use if SimianGrid is -;; installed at http://www.mygrid.com/Grid/ -;; -; AssetServerURI = "http://www.mygrid.com/Grid/?id=" -; InventoryServerURI = "http://www.mygrid.com/Grid/" -; AvatarServerURI = "http://www.mygrid.com/Grid/" -; PresenceServerURI = "http://www.mygrid.com/Grid/" -; UserAccountServerURI = "http://www.mygrid.com/Grid/" -; AuthenticationServerURI = "http://www.mygrid.com/Grid/" -; FriendsServerURI = "http://www.mygrid.com/Grid/" -; GroupsServerURI = "http://www.mygrid.com/Grid/" - -[Includes] - Include-Common = "config-include/GridCommon.ini" - -[Modules] - GridServices = "OpenSim.Services.Connectors.dll:SimianGridServiceConnector" - PresenceServices = "OpenSim.Services.Connectors.dll:SimianPresenceServiceConnector" - UserAccountServices = "OpenSim.Services.Connectors.dll:SimianUserAccountServiceConnector" - AuthenticationServices = "OpenSim.Services.Connectors.dll:SimianAuthenticationServiceConnector" - AssetServices = "OpenSim.Services.Connectors.dll:SimianAssetServiceConnector" - InventoryServices = "OpenSim.Services.Connectors.dll:SimianInventoryServiceConnector" - AvatarServices = "OpenSim.Services.Connectors.dll:SimianAvatarServiceConnector" - - NeighbourServices = "RemoteNeighbourServicesConnector" - SimulationServices = "RemoteSimulationConnectorModule" - EntityTransferModule = "BasicEntityTransferModule" - InventoryAccessModule = "BasicInventoryAccessModule" - - LandServiceInConnector = true - NeighbourServiceInConnector = true - SimulationServiceInConnector = true - LibraryModule = false - - AssetCaching = "FlotsamAssetCache" +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +;; Start IntSim Service Configuration +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Friends] - Connector = "OpenSim.Services.Connectors.dll:SimianFriendsServiceConnector" +Connector = "OpenSim.Services.Connectors.dll:SimianFriendsServiceConnector" +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [GridService] - LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" - StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" +LocalServiceModule = "OpenSim.Services.GridService.dll:GridService" +StorageProvider = "OpenSim.Data.Null.dll:NullRegionData" +GridServerURI = "http://grid.beta.sciencesim.com/Grid/" +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [LibraryService] - LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" - LibraryName = "OpenSim Library" - DefaultLibrary = "./inventory/Libraries.xml" +LocalServiceModule = "OpenSim.Services.InventoryService.dll:LibraryService" +LibraryName = "OpenSim Library" +DefaultLibrary = "./inventory/Libraries.xml" +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [AssetService] - DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" - AssetLoaderArgs = "assets/AssetSets.xml" - +DefaultAssetLoader = "OpenSim.Framework.AssetLoader.Filesystem.dll" +AssetLoaderArgs = "assets/AssetSets.xml" +AssetServerURI = "http://grid.beta.sciencesim.com/Grid/?id=" + +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +[InventoryService] +InventoryServerURI = "http://grid.beta.sciencesim.com/Grid/" + +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +[AvatarService] +AvatarServerURI = "http://grid.beta.sciencesim.com/Grid/" + +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +[PresenceService] +PresenceServerURI = "http://grid.beta.sciencesim.com/Grid/" + +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +[UserAccountService] +UserAccountServerURI = "http://grid.beta.sciencesim.com/Grid/" + +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +[AuthenticationService] +AuthenticationServerURI = "http://grid.beta.sciencesim.com/Grid/" + +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +[FriendsService] +FriendsServerURI = "http://grid.beta.sciencesim.com/Grid/" + +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX [Groups] - Enabled = true - Module = GroupsModule - DebugEnabled = false - NoticesEnabled = true - MessagingModule = GroupsMessagingModule - MessagingEnabled = true - ServicesConnectorModule = SimianGroupsServicesConnector +ServicesConnectorModule = SimianGroupsServicesConnector +GroupsServerURI = "http://grid.beta.sciencesim.com/Grid/" + +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +[Modules] +GridServices = "OpenSim.Services.Connectors.dll:SimianGridServiceConnector" +PresenceServices = "OpenSim.Services.Connectors.dll:SimianPresenceServiceConnector" +UserAccountServices = "OpenSim.Services.Connectors.dll:SimianUserAccountServiceConnector" +AuthenticationServices = "OpenSim.Services.Connectors.dll:SimianAuthenticationServiceConnector" +AssetServices = "OpenSim.Services.Connectors.dll:SimianAssetServiceConnector" +InventoryServices = "OpenSim.Services.Connectors.dll:SimianInventoryServiceConnector" +AvatarServices = "OpenSim.Services.Connectors.dll:SimianAvatarServiceConnector" + +NeighbourServices = "RemoteNeighbourServicesConnector" +SimulationServices = "RemoteSimulationConnectorModule" +EntityTransferModule = "BasicEntityTransferModule" +InventoryAccessModule = "BasicInventoryAccessModule" + +LandServiceInConnector = true +NeighbourServiceInConnector = true +SimulationServiceInConnector = true +LibraryModule = false + +AssetCaching = "FlotsamAssetCache" +Include-FlotsamCache = "config-include/FlotsamCache.ini" + +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +[Architecture@replace] +;; This eliminates the existing Architecture section + +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +;; Finish ScienceSim Service Configuration +;; XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX diff --git a/prebuild.xml b/prebuild.xml index 5cc742c45a..37e3a98855 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -459,6 +459,7 @@ +