OpenSimMirror/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/RegionSyncClient.cs

1222 lines
61 KiB
C#

/*
* 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 copyrightD
* 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;
using System.IO;
using System.Net;
using System.Net.Sockets;
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;
using TPFlags = OpenSim.Framework.Constants.TeleportFlags;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes.Types;
using log4net;
namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
{
// The RegionSyncClient has a receive thread to process messages from the RegionSyncServer.
public class RegionSyncClient
{
#region MsgHandlerStatus Enum
public enum MsgHandlerStatus
{
Success, // Everything went as expected
Trivial, // Minor issue, nothing to worry about
Warning, // Something went wrong, we can continue
Error // This should certainly not have happened! (A bug)
}
#endregion
#region RegionSyncClient members
// Set the addr and port of RegionSyncServer
private IPAddress m_addr;
private Int32 m_port;
// A reference to the local scene
private Scene m_scene;
// The avatars added to this client manager for clients on other client managers
object m_syncRoot = new object();
Dictionary<UUID, RegionSyncAvatar> m_remoteAvatars = new Dictionary<UUID, RegionSyncAvatar>();
Dictionary<UUID, IClientAPI> m_localAvatars = new Dictionary<UUID, IClientAPI>();
//private bool m_symSync = false;
private Dictionary<UUID, RegionSyncAvatar> RemoteAvatars
{
get { return m_remoteAvatars; }
}
private Dictionary<UUID, IClientAPI> LocalAvatars
{
get { return m_localAvatars; }
}
// The logfile
private ILog m_log;
private string LogHeader()
{
return String.Format("[REGION SYNC CLIENT ({0})]", m_scene.RegionInfo.RegionName);
}
// The listener and the thread which listens for connections from client managers
private Thread m_rcvLoop;
// The client connection to the RegionSyncServer
private TcpClient m_client = new TcpClient();
private string m_regionName;
private System.Timers.Timer m_statsTimer = new System.Timers.Timer(30000);
//KittyL: added to identify different actors
private ActorType m_actorType = ActorType.ClientManager;
// The queue of incoming messages which need handling
//private Queue<string> m_inQ = new Queue<string>();
#endregion
// Constructor
public RegionSyncClient(Scene scene, string addr, int port)
{
m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
m_scene = scene;
m_addr = IPAddress.Parse(addr);
m_port = port;
m_statsTimer.Elapsed += new System.Timers.ElapsedEventHandler(StatsTimerElapsed);
}
// Start the RegionSyncClient
public void Start()
{
try
{
m_client.Connect(m_addr, m_port);
}
catch (Exception e)
{
m_log.WarnFormat("{0} [Start] Could not connect to RegionSyncServer at {1}:{2}", LogHeader(), m_addr, m_port);
m_log.Warn(e.Message);
}
m_log.WarnFormat("{0} Connected to RegionSyncServer at {1}:{2}", LogHeader(), m_addr, m_port);
m_rcvLoop = new Thread(new ThreadStart(ReceiveLoop));
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());
DoInitialSync();
m_statsTimer.Start();
}
// Disconnect from the RegionSyncServer and close the RegionSyncClient
public void Stop()
{
// The remote scene will remove our avatars automatically when we disconnect
m_rcvLoop.Abort();
ShutdownClient();
}
public void ReportStatus()
{
Dictionary<UUID, IClientAPI> locals = LocalAvatars;
Dictionary<UUID, RegionSyncAvatar> remotes = RemoteAvatars;
m_log.WarnFormat("{0}: {1,4} Local avatars", LogHeader(), locals.Count);
foreach (KeyValuePair<UUID, IClientAPI> kvp in locals)
{
ScenePresence sp;
bool inScene = m_scene.TryGetScenePresence(kvp.Value.AgentId, out sp);
m_log.WarnFormat("{0}: {1} {2} {3}", LogHeader(), kvp.Value.AgentId, kvp.Value.Name, inScene ? "" : "(NOT IN SCENE)");
}
m_log.WarnFormat("{0}: {1,4} Remote avatars", LogHeader(), remotes.Count);
foreach (KeyValuePair<UUID, RegionSyncAvatar> kvp in remotes)
{
ScenePresence sp;
bool inScene = m_scene.TryGetScenePresence(kvp.Value.AgentId, out sp);
m_log.WarnFormat("{0}: {1} {2} {3}", LogHeader(), kvp.Value.AgentId, kvp.Value.Name, inScene ? "" : "(NOT IN SCENE)");
}
m_log.WarnFormat("{0}: ===================================================", LogHeader());
m_log.WarnFormat("{0} Synchronized to RegionSyncServer at {1}:{2}", LogHeader(), m_addr, m_port);
m_log.WarnFormat("{0}: {1,4} Local avatars", LogHeader(), locals.Count);
m_log.WarnFormat("{0}: {1,4} Remote avatars", LogHeader(), remotes.Count);
}
private void ShutdownClient()
{
m_log.WarnFormat("{0} Disconnected from RegionSyncServer. Shutting down.", LogHeader());
// Remove remote avatars from local scene
try
{
foreach (KeyValuePair<UUID, RegionSyncAvatar> kvp in RemoteAvatars)
{
// This will cause a callback into RemoveLocalClient
m_scene.RemoveClient(kvp.Key);
}
}
catch (Exception e)
{
m_log.ErrorFormat("Caught exception while removing remote avatars on Shutdown: {0}", e.Message);
}
try
{
// Remove local avatars from remote scene
foreach (KeyValuePair<UUID, IClientAPI> 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.OnSameRegionTeleportlRequest -= HandleAgentSameRegionTeleport;
}
}
catch (Exception e)
{
m_log.ErrorFormat("Caught exception while removing local avatars on Shutdown: {0}", e.Message);
}
// Close the connection
m_client.Client.Close();
m_client.Close();
}
// Listen for messages from a RegionSyncClient
// *** This is the main thread loop for each connected client
private void ReceiveLoop()
{
m_log.WarnFormat("{0} Thread running: {1}", LogHeader(), m_rcvLoop.Name);
while (true && m_client.Connected)
{
RegionSyncMessage msg;
// Try to get the message from the network stream
try
{
msg = new RegionSyncMessage(m_client.GetStream());
//m_log.WarnFormat("{0} Received: {1}", LogHeader(), msg.ToString());
}
// If there is a problem reading from the client, shut 'er down.
catch
{
ShutdownClient();
return;
}
// Try handling the message
try
{
HandleMessage(msg);
}
catch (Exception e)
{
m_log.WarnFormat("{0} Encountered an exception: {1} (MSGTYPE = {2})", LogHeader(), e.Message, msg.ToString());
}
}
}
#region SEND
// Send a message to a single connected RegionSyncClient
private void Send(string msg)
{
byte[] bmsg = System.Text.Encoding.ASCII.GetBytes(msg + System.Environment.NewLine);
Send(bmsg);
}
private void Send(RegionSyncMessage msg)
{
Send(msg.ToBytes());
//m_log.WarnFormat("{0} Sent {1}", LogHeader(), msg.ToString());
}
private void Send(byte[] data)
{
if (m_client.Connected)
{
try
{
m_client.GetStream().Write(data, 0, data.Length);
}
// If there is a problem reading from the client, shut 'er down.
// *** Still need to alert the module that it's no longer connected!
catch
{
ShutdownClient();
}
}
}
#endregion
// Send coarse locations to locally connected clients
// This is a highly optimized version for lots of local and remote clients
// 99.9% faster for 1000 clients!
public void SendCoarseLocations()
{
List<UUID> ids = new List<UUID>();
List<Vector3> locations = new List<Vector3>();
m_scene.GetCoarseLocations(out ids, out locations);
foreach (KeyValuePair<UUID, IClientAPI> kvp in LocalAvatars)
{
kvp.Value.SendCoarseLocationUpdate(ids, locations);
}
}
public void SendAppearanceToScene(UUID agentID)
{
OSDMap data = new OSDMap();
data["id"] = OSD.FromUUID(agentID);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AvatarAppearance, OSDParser.SerializeJsonString(data)));
}
// Handle an incoming message
// TODO: This should not be synchronous with the receive!
// Instead, handle messages from an incoming Queue so server doesn't block sending
private void HandleMessage(RegionSyncMessage msg)
{
switch (msg.Type)
{
case RegionSyncMessage.MsgType.RegionName:
{
m_regionName = Encoding.ASCII.GetString(msg.Data, 0, msg.Length);
RegionSyncMessage.HandleSuccess(LogHeader(), msg, String.Format("Syncing to region \"{0}\"", m_regionName));
return;
}
case RegionSyncMessage.MsgType.NewAvatar:
{
// Get the data from message and error check
OSDMap data = DeserializeMessage(msg);
if (data == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Could not deserialize JSON data.");
return;
}
// Get the parameters from data and error check
UUID agentID = data["agentID"].AsUUID();
string first = data["first"].AsString();
string last = data["last"].AsString();
uint localID = data["localID"].AsUInteger();
Vector3 startPos = data["startPos"].AsVector3();
if (agentID == null || agentID == UUID.Zero || first == null || last == null || startPos == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Missing or invalid JSON data.");
return;
}
m_log.DebugFormat("{0} Handle NewAvatar for \"{1} {2}\"", LogHeader(), first, last);
if (m_remoteAvatars.ContainsKey(agentID))
{
RegionSyncMessage.HandleWarning(LogHeader(), msg, String.Format("Attempted to add duplicate avatar \"{0} {1}\" ({2})", first, last, agentID.ToString()));
return;
}
ScenePresence sp;
if (m_scene.TryGetScenePresence(agentID, out sp))
{
//RegionSyncMessage.HandlerDebug(LogHeader(), msg, String.Format("Confirmation of new avatar \"{0}\" ({1})", sp.Name, sp.UUID.ToString()));
RegionSyncMessage.HandleSuccess(LogHeader(), msg, String.Format("Confirmation of new avatar \"{0}\" ({1})", sp.Name, sp.UUID.ToString()));
return;
}
RegionSyncAvatar av = new RegionSyncAvatar(m_scene, agentID, first, last, startPos);
lock (m_syncRoot)
{
Dictionary<UUID, RegionSyncAvatar> newremotes = new Dictionary<UUID, RegionSyncAvatar>(RemoteAvatars);
// Add to list of remote avatars
newremotes.Add(agentID, av);
m_remoteAvatars = newremotes;
//m_remoteAvatars.Add(agentID, av);
//m_scene.AddNewClient(av);
m_scene.AddNewClient2(av, true, false);
m_scene.TryGetScenePresence(agentID, out sp);
if (sp == null)
{
m_log.ErrorFormat("{0} Could not get newly added scene presence.", LogHeader());
}
else
{
sp.IsSyncedAvatar = true;
}
}
//RegionSyncMessage.HandlerDebug(LogHeader(), msg, String.Format("Added new remote avatar \"{0}\" ({1})", first + " " + last, agentID));
RegionSyncMessage.HandleSuccess(LogHeader(), msg, String.Format("Added new remote avatar \"{0}\" ({1})", first + " " + last, agentID));
return;
}
case RegionSyncMessage.MsgType.AvatarTeleportSameRegion:
{
if (PhysEngineToSceneConnectorModule.IsPhysEngineActorS)
{
OSDMap data = DeserializeMessage(msg);
if (data == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Could not deserialize JSON data.");
return;
}
// Get the parameters from data and error check
UUID agentID = UUID.Zero;
Vector3 pos = Vector3.Zero;
try
{
agentID = data["id"].AsUUID();
pos = data["pos"].AsVector3();
}
catch (Exception e)
{
m_log.ErrorFormat("{0} Caught exception in UpdatedAvatar handler (Decoding JSON): {1}", LogHeader(), e.Message);
}
// Find the presence in the scene and error check
ScenePresence presence;
m_scene.TryGetScenePresence(agentID, out presence);
if (presence == null)
{
//RegionSyncMessage.HandleWarning(LogHeader(), msg, String.Format("agentID {0} not found.", agentID.ToString()));
return;
}
if (presence.IsBalancing && presence.ControllingClient is RegionSyncAvatar)
{
lock (m_syncRoot)
{
if (m_localAvatars.ContainsKey(presence.UUID))
{
}
else
{
m_log.WarnFormat("{0} Received update for balancing avatar not in local avatar list. \"{1}\"", LogHeader(), presence.Name);
return;
}
}
}
presence.Teleport(pos);
//m_log.DebugFormat("Received AvatarTeleportSameRegion: Teleport {0} to pos {1}, now pa.pos = {2}", presence.Name, pos, presence.PhysicsActor.Position);
}
return;
}
case RegionSyncMessage.MsgType.UpdatedAvatar:
{
// Get the data from message and error check
OSDMap data = DeserializeMessage(msg);
if (data == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Could not deserialize JSON data.");
return;
}
// Get the parameters from data and error check
UUID agentID = UUID.Zero;
Vector3 pos = Vector3.Zero;
Vector3 vel = Vector3.Zero;
Quaternion rot = Quaternion.Identity;
bool flying = false;
string anim = "";
uint flags = 0;
Vector4 collisionPlane = Vector4.Zero;
Vector3 offsetPosition = Vector3.Zero;
uint parentID = 0;
bool doFullUpdate = false;
try
{
agentID = data["id"].AsUUID();
pos = data["pos"].AsVector3();
vel = data["vel"].AsVector3();
rot = data["rot"].AsQuaternion();
flying = data["fly"].AsBoolean();
anim = data["anim"].AsString();
flags = data["flags"].AsUInteger();
// do we have the data for a full update?
OSD valTemp = null;
data.TryGetValue("coll", out valTemp);
if (valTemp != null)
{
collisionPlane = valTemp.AsVector4();
offsetPosition = data["off"].AsVector3();
parentID = data["pID"].AsUInteger();
doFullUpdate = true;
}
}
catch (Exception e)
{
m_log.ErrorFormat("{0} Caught exception in UpdatedAvatar handler (Decoding JSON): {1}", LogHeader(), e.Message);
}
if (agentID == null || agentID == UUID.Zero )
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Missing or invalid JSON data.");
return;
}
// m_log.WarnFormat("{0}: UpdatedAvatar JSON: {1}", LogHeader(), data.ToString());
// Find the presence in the scene and error check
ScenePresence presence;
m_scene.TryGetScenePresence(agentID, out presence);
if (presence == null)
{
//RegionSyncMessage.HandleWarning(LogHeader(), msg, String.Format("agentID {0} not found.", agentID.ToString()));
return;
}
if (presence.IsBalancing && presence.ControllingClient is RegionSyncAvatar)
{
lock (m_syncRoot)
{
if (m_localAvatars.ContainsKey(presence.UUID))
{
}
else
{
m_log.WarnFormat("{0} Received update for balancing avatar not in local avatar list. \"{1}\"", LogHeader(), presence.Name);
return;
}
}
}
/**
// 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 ));
*/
try
{
if (!PhysEngineToSceneConnectorModule.IsPhysEngineActorS)
{
presence.AgentControlFlags = flags;
presence.AbsolutePosition = pos;
//m_log.DebugFormat("{0}: UpdateAvatar. Setting vel={1}, AbsolutePosition = {1}", LogHeader(), vel, presence.AbsolutePosition);
presence.Velocity = vel;
presence.Rotation = rot;
// It seems the physics scene can drop an avatar if the avatar makes it angry
if (presence.PhysicsActor != null)
{
presence.PhysicsActor.Flying = flying;
presence.PhysicsActor.CollidingGround = !flying;
}
if (doFullUpdate)
{
presence.CollisionPlane = collisionPlane;
presence.OffsetPosition = offsetPosition;
presence.ParentID = parentID;
}
}
}
catch(Exception e)
{
m_log.ErrorFormat("{0} Caught exception in UpdatedAvatar handler (setting presence values) for {1}: {2}", LogHeader(), presence.Name, e.Message);
}
try
{
if (doFullUpdate)
{
presence.SendAvatarDataToAllAgents();
}
else
{
presence.SendTerseUpdateToAllClients();
}
}
catch (Exception e)
{
m_log.ErrorFormat("{0} Caught exception in UpdatedAvatar handler (SendTerseUpdateToAllClients): {1}", LogHeader(), e.Message);
}
/*
foreach (KeyValuePair<UUID, IClientAPI> kvp in LocalAvatars)
{
presence.SendTerseUpdateToClient(kvp.Value);
}*/
try
{
// If the animation has changed, set the new animation
if (!presence.Animator.CurrentMovementAnimation.Equals(anim))
presence.Animator.TrySetMovementAnimation(anim);
}
catch (Exception e)
{
m_log.ErrorFormat("{0} Caught exception in UpdatedAvatar handler (TrySetMovementAnimation): {1}", LogHeader(), e.Message);
}
/*
string 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");
* */
// RegionSyncMessage.HandleSuccess(LogHeader(), msg, result);
return;
}
case RegionSyncMessage.MsgType.RemovedAvatar:
{
// Get the data from message and error check
OSDMap data = DeserializeMessage(msg);
if (data == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Could not deserialize JSON data.");
return;
}
// Get the agentID from data and error check
UUID agentID = data["agentID"].AsUUID();
if (agentID == UUID.Zero)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Missing or invalid JSON data.");
return;
}
// Try to get the presence from the scene
ScenePresence presence;
m_scene.TryGetScenePresence(agentID, out presence);
// Local and Remote avatar dictionaries will only be modified under this lock
// They can be read without locking as we are just going to swap refs atomically
lock (m_syncRoot)
{
if (RemoteAvatars.ContainsKey(agentID))
{
Dictionary<UUID, RegionSyncAvatar> newremotes = new Dictionary<UUID, RegionSyncAvatar>(RemoteAvatars);
string name = newremotes[agentID].Name;
// Remove from list of remote avatars
newremotes.Remove(agentID);
m_remoteAvatars = newremotes;
// If this presence is in out remote list, then this message tells us to remove it from the
// local scene so we should find it there.
if (presence == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, String.Format("Couldn't remove remote avatar \"{0}\" because it's not in local scene", name));
return;
}
m_scene.RemoveClient(agentID);
RegionSyncMessage.HandleSuccess(LogHeader(), msg, String.Format("Remote avatar \"{0}\" removed from scene", name));
return;
}
else if (LocalAvatars.ContainsKey(agentID))
{
Dictionary<UUID, IClientAPI> newlocals = new Dictionary<UUID, IClientAPI>(LocalAvatars);
string name = newlocals[agentID].Name;
// Remove from list of local avatars
newlocals.Remove(agentID);
m_localAvatars = newlocals;
// If this presence is in our local list, then this message is a confirmation from the server
// and the presence should not be in our scene at this time.
if (presence != null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, String.Format("Remove avatar confirmation received for \"{0}\" still in the local scene.", name));
return;
}
RegionSyncMessage.HandleSuccess(LogHeader(), msg, String.Format("Received confirmation of removed avatar \"{0}\"", name));
return;
}
}
// Getting here is always an error because we have been asked to remove and avatar
// so it should have been in either the local or remote collections
if (presence == null)
RegionSyncMessage.HandleError(LogHeader(), msg, String.Format("Avatar is not local OR remote and was not found in scene: \"{0}\" {1}", presence.Name, agentID.ToString()));
else
RegionSyncMessage.HandleError(LogHeader(), msg, String.Format("Avatar is not local OR remote but was found in scene: {1}", agentID.ToString()));
return;
}
case RegionSyncMessage.MsgType.ChatFromClient:
{
// Get the data from message and error check
OSDMap data = DeserializeMessage(msg);
if (data == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Could not deserialize JSON data.");
return;
}
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();
// m_log.DebugFormat("{0} chat. chan={1}, from='{2}', msg='{3}'", LogHeader(), args.Channel, args.From, args.Message);
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);
}
else {
args.Sender = null;
args.SenderUUID = id;
m_scene.EventManager.TriggerOnChatFromWorld(null, args);
}
//RegionSyncMessage.HandlerDebug(LogHeader(), msg, String.Format("Received chat from \"{0}\"", args.From));
RegionSyncMessage.HandleSuccess(LogHeader(), msg, String.Format("Received chat from \"{0}\"", args.From));
return;
}
case RegionSyncMessage.MsgType.AvatarAppearance:
{
m_log.DebugFormat("{0} START of AvatarAppearance handler", LogHeader());
// Get the data from message and error check
OSDMap data = DeserializeMessage(msg);
if (data == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Could not deserialize JSON data.");
return;
}
// Get the parameters from data and error check
UUID agentID = data["id"].AsUUID();
if (agentID == null || agentID == UUID.Zero)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Missing or invalid JSON data.");
return;
}
// If we are receiving appearance for a presence connected here, ignore it.
// This is just a confirmation of appearance we sent to scene.
if (m_localAvatars.ContainsKey(agentID))
return;
// Tells the avatar factory to pull an updated appearance from the avatar service
m_scene.AvatarFactory.RefreshAppearance(agentID);
m_log.DebugFormat("{0} END of AvatarAppearance handler", LogHeader());
return;
}
case RegionSyncMessage.MsgType.SitResponse:
{
OSDMap data = DeserializeMessage(msg);
if (data == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Could not deserialize JSON data.");
return;
}
UUID agentID = data["agentID"].AsUUID();
m_log.DebugFormat("{0} SitResponse for {1}", LogHeader(), agentID);
UUID targetID = data["targetID"].AsUUID();
Vector3 offsetPos = data["offsetPos"].AsVector3();
Quaternion sitOrientation = data["sitOrientation"].AsQuaternion();
Boolean autopilot = data["autoPilot"].AsBoolean();
Vector3 cameraAtOffset = data["cameraAtOffset"].AsVector3();
Vector3 cameraEyeOffset = data["cameraEyeOffset"].AsVector3();
Boolean forceMouseLook = data["forceMouseLook"].AsBoolean();
ScenePresence presence;
if (m_scene.TryGetScenePresence(agentID, out presence))
{
presence.ControllingClient.SendSitResponse(targetID, offsetPos, sitOrientation, autopilot,
cameraAtOffset, cameraEyeOffset, forceMouseLook);
}
else
{
RegionSyncMessage.HandleWarning(LogHeader(), msg,
String.Format("Agent {0} not found in the scene for SitResponse.", agentID));
}
return;
}
case RegionSyncMessage.MsgType.SendAnimations:
{
OSDMap data = DeserializeMessage(msg);
if (data == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Could not deserialize JSON data.");
return;
}
UUID agentID = data["agentID"].AsUUID();
// m_log.DebugFormat("{0} SendAnimations for {1}", LogHeader(), agentID.ToString());
OSDArray animatA = (OSDArray)data["animations"];
UUID[] animIDs = new UUID[animatA.Count];
for (int ii = 0; ii < animatA.Count; ii++) animIDs[ii] = animatA[ii].AsUUID();
OSDArray seqsA = (OSDArray)data["seqs"];
int[] seqs = new int[seqsA.Count];
for (int jj = 0; jj < seqsA.Count; jj++) seqs[jj] = seqsA[jj].AsInteger();
UUID sourceAgentID = data["sourceAgentID"].AsUUID();
OSDArray obIDA = (OSDArray)data["objectIDs"];
UUID[] objectIDs = new UUID[obIDA.Count];
for (int kk = 0; kk < obIDA.Count; kk++) objectIDs[kk] = obIDA[kk].AsUUID();
ScenePresence sp;
m_scene.TryGetScenePresence(agentID, out sp);
if (sp != null)
{
if (agentID == sourceAgentID)
{
sp.Animator.SendAnimPack(animIDs, seqs, objectIDs);
}
else
{
sp.ControllingClient.SendAnimations(animIDs, seqs, sourceAgentID, objectIDs);
}
}
/*
else
{
m_log.WarnFormat("{0} Could not send animation for {1} because scene presence not found",
LogHeader(), agentID.ToString());
}
*/
return;
}
case RegionSyncMessage.MsgType.BalanceClientLoad:
{
// Get the data from message and error check
OSDMap data = DeserializeMessage(msg);
if (data == null)
{
RegionSyncMessage.HandleError(LogHeader(), msg, "Could not deserialize JSON data.");
return;
}
int targetLoad = data["endCount"].AsInteger();
string destinationRegion = data["toRegion"].AsString();
RegionSyncMessage.HandleSuccess(LogHeader(), msg, String.Format("Balancing load from {0} to {1} via region \"{2}\"", m_localAvatars.Count, targetLoad, destinationRegion));
BalanceClientLoad(targetLoad, destinationRegion);
return;
}
default:
{
RegionSyncMessage.HandleError(LogHeader(), msg, String.Format("{0} Unsupported message type: {1}", LogHeader(), ((int)msg.Type).ToString()));
return;
}
}
}
HashSet<string> exceptions = new HashSet<string>();
private OSDMap DeserializeMessage(RegionSyncMessage msg)
{
OSDMap data = null;
try
{
data = OSDParser.DeserializeJson(Encoding.ASCII.GetString(msg.Data, 0, msg.Length)) as OSDMap;
}
catch (Exception e)
{
lock (exceptions)
// If this is a new message, then print the underlying data that caused it
if (!exceptions.Contains(e.Message))
m_log.Error(LogHeader() + " " + Encoding.ASCII.GetString(msg.Data, 0, msg.Length));
data = null;
}
return data;
}
private void DoInitialSync()
{
//m_scene.DeleteAllSceneObjects();
//KittyL: added to distinguish different actors
//Send(new RegionSyncMessage(RegionSyncMessage.MsgType.ActorType, m_actorType.ToString()));
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.RegionName, m_scene.RegionInfo.RegionName));
m_log.WarnFormat("Sending region name: \"{0}\"", m_scene.RegionInfo.RegionName);
//DSG SYNC: commenting out terrian and object updates
/*
if (!m_symSync)
{
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.GetTerrain));
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.GetObjects));
}
* */
//end of DSG SYNC
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.GetAvatars));
// Register for events which will be forwarded to authoritative scene
// m_scene.EventManager.OnNewClient += EventManager_OnNewClient;
m_scene.EventManager.OnMakeRootAgent += EventManager_OnMakeRootAgent;
m_scene.EventManager.OnMakeChildAgent += EventManager_OnMakeChildAgent;
m_scene.EventManager.OnClientClosed += new EventManager.ClientClosed(RemoveLocalClient);
}
public string GetServerAddressAndPort()
{
return m_addr.ToString() + ":" + m_port.ToString();
}
#region MESSAGES SENT FROM CLIENT MANAGER TO SIM
// public void EventManager_OnNewClient(IClientAPI client)
public void EventManager_OnMakeRootAgent(ScenePresence scenep)
{
IClientAPI client = scenep.ControllingClient;
// If this client was added in response to NewAvatar message from a synced server,
// don't subscribe to events or send back to server
if (RemoteAvatars.ContainsKey(client.AgentId))
return;
// If this client is local, then it must have been added during a teleport due to load balancing.
// Don't subscribe to events or sent to server.
if (LocalAvatars.ContainsKey(client.AgentId))
return;
// Otherwise, it's a real local client connecting so track it locally
lock (m_syncRoot)
{
Dictionary<UUID, IClientAPI> newlocals = new Dictionary<UUID, IClientAPI>(LocalAvatars);
newlocals.Add(client.AgentId, client);
m_localAvatars = newlocals;
}
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
OSDMap data = new OSDMap(4);
data["agentID"] = OSD.FromUUID(client.AgentId);
data["first"] = OSD.FromString(client.FirstName);
data["last"] = OSD.FromString(client.LastName);
// data["startPos"] = OSD.FromVector3(client.StartPos);
data["startPos"] = OSD.FromVector3(scenep.AbsolutePosition);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentAdd, OSDParser.SerializeJsonString(data)));
// Register for interesting client events which will be forwarded to auth sim
// These are the raw packet data blocks from the client, intercepted and sent up to the sim
client.OnAgentUpdateRaw += HandleAgentUpdateRaw;
client.OnSameRegionTeleportlRequest += HandleAgentSameRegionTeleport;
//DSG SYNC: do not subscribe to OnChatFromClientRaw: RegionSyncModule + Scene.EventManager will handle this.
//client.OnChatFromClientRaw += HandleChatFromClientRaw;
client.OnAgentRequestSit += HandleAgentRequestSit;
client.OnAgentSit += HandleAgentSit;
//DSG SYNC: do not subscribe to OnGrabObject, OnGrabUpdate, and OnDeGrabObject: RegionSyncModule + Scene.EventManager will handle this.
//client.OnGrabObject += HandleGrabObject;
//client.OnGrabUpdate += HandleGrabUpdate;
//client.OnDeGrabObject += HandleDeGrabObject;
client.OnStartAnim += HandleStartAnim;
client.OnStopAnim += HandleStopAnim;
}
public void EventManager_OnMakeChildAgent(ScenePresence scenep)
{
// if demoting from root, tell the server not to track our child
m_log.DebugFormat("{0} Demotion to child. Sending remove for {1}/{2}",
LogHeader(), scenep.ControllingClient.Name, scenep.ControllingClient.AgentId);
OSDMap data = new OSDMap(1);
data["agentID"] = OSD.FromUUID(scenep.ControllingClient.AgentId);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentRemove, OSDParser.SerializeJsonString(data)));
// shouldn't we unsubscribe from all the events?
return;
}
private void RemoveLocalClient(UUID clientID, Scene scene)
{
// If the client closed due to load balancing, the presence will stick around
// and we just move from a local avatar to a remote avatar and give it a new
// RegionSyncAvatar as the client
ScenePresence sp;
m_scene.TryGetScenePresence(clientID, out sp);
if (sp != null && sp.IsBalancing)
{
// Replace the LLClientView with a RegionSyncAvatar
RegionSyncAvatar av = new RegionSyncAvatar(sp.Scene, sp.UUID, sp.Firstname, sp.Lastname, sp.AbsolutePosition);
sp.ControllingClient = av;
lock (m_syncRoot)
{
Dictionary<UUID, IClientAPI> newlocals = new Dictionary<UUID, IClientAPI>(LocalAvatars);
newlocals.Remove(sp.UUID);
m_localAvatars = newlocals;
Dictionary<UUID, RegionSyncAvatar> newremotes = new Dictionary<UUID, RegionSyncAvatar>(RemoteAvatars);
newremotes.Add(sp.UUID, av);
m_remoteAvatars = newremotes;
}
sp.IsBalancing = false;
sp.IsSyncedAvatar = true;
return;
}
lock (m_syncRoot)
{
// If the client to be removed is a local client, then send a message to the remote scene
if (LocalAvatars.ContainsKey(clientID))
{
// If we are still connected, let the auth sim know that an agent has disconnected
if (m_client.Connected)
{
m_log.WarnFormat("{0} Local client ({1}) has left the scene. Notifying remote scene.", LogHeader(), clientID.ToString());
OSDMap data = new OSDMap(1);
data["agentID"] = OSD.FromUUID(clientID);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentRemove, OSDParser.SerializeJsonString(data)));
}
else
{
m_log.WarnFormat("{0} Local client ({1}) has left the scene.", LogHeader(), clientID.ToString());
// Remove it from our list of local avatars (we will never receive a confirmation since we are disconnected)
Dictionary<UUID, IClientAPI> newlocals = new Dictionary<UUID, IClientAPI>(LocalAvatars);
newlocals.Remove(clientID);
m_localAvatars = newlocals;
}
}
}
}
/// <summary>
/// This is the event handler for client movement. If a client is moving, this event is triggering.
/// </summary>
public void HandleAgentUpdateRaw(object sender, byte[] agentData)
{
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentUpdate, agentData));
}
//KittyL: Added to support teleporting within the same region
public void HandleAgentSameRegionTeleport(object sender, byte[] tpLocReq)
{
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentSameRegionTeleport, tpLocReq));
}
public void HandleAgentRequestSit(object sender, UUID agentID, UUID targetID, Vector3 offset)
{
// m_log.DebugFormat("{0} HandleAgentRequestSit for {1}", LogHeader(), agentID.ToString());
OSDMap data = new OSDMap(3);
data["agentID"] = OSD.FromUUID(agentID);
data["targetID"] = OSD.FromUUID(targetID);
data["offset"] = OSD.FromVector3(offset);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentRequestSit, OSDParser.SerializeJsonString(data)));
}
public void HandleAgentSit(IClientAPI remoteClient, UUID agentID)
{
// m_log.DebugFormat("{0} HandleAgentSit for {1}", LogHeader(), agentID.ToString());
OSDMap data = new OSDMap(1);
data["agentID"] = OSD.FromUUID(agentID);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AgentSit, OSDParser.SerializeJsonString(data)));
}
public void HandleGrabObject(uint localID, Vector3 offsetPos, IClientAPI remoteClient, List<SurfaceTouchEventArgs> surfaceArgs)
{
// m_log.DebugFormat("{0} HandleGrabObject for {1}", LogHeader(), remoteClient.AgentId.ToString());
OSDMap data = new OSDMap(4);
data["agentID"] = OSD.FromUUID(remoteClient.AgentId);
data["localID"] = OSD.FromUInteger(localID);
data["offsetPos"] = OSD.FromVector3(offsetPos);
data["surfaceArgs"] = MakeSurfaceArgsArray(surfaceArgs);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.GrabObject, OSDParser.SerializeJsonString(data)));
}
public void HandleGrabUpdate(UUID objectID, Vector3 offset, Vector3 pos, IClientAPI remoteClient, List<SurfaceTouchEventArgs> surfaceArgs)
{
// m_log.DebugFormat("{0} HandleGrabUpdate for {1}", LogHeader(), remoteClient.AgentId.ToString());
OSDMap data = new OSDMap(5);
data["agentID"] = OSD.FromUUID(remoteClient.AgentId);
data["objectID"] = OSD.FromUUID(objectID);
data["offset"] = OSD.FromVector3(offset);
data["pos"] = OSD.FromVector3(pos);
data["surfaceArgs"] = MakeSurfaceArgsArray(surfaceArgs);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.GrabUpdate, OSDParser.SerializeJsonString(data)));
}
public void HandleDeGrabObject(uint localID, IClientAPI remoteClient, List<SurfaceTouchEventArgs> surfaceArgs)
{
// m_log.DebugFormat("{0} HandleDeGrabObject for {1}", LogHeader(), remoteClient.AgentId.ToString());
OSDMap data = new OSDMap(3);
data["agentID"] = OSD.FromUUID(remoteClient.AgentId);
data["localID"] = OSD.FromUInteger(localID);
data["surfaceArgs"] = MakeSurfaceArgsArray(surfaceArgs);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.DeGrabObject, OSDParser.SerializeJsonString(data)));
}
public void HandleStartAnim(IClientAPI remoteClient, UUID animID)
{
// m_log.DebugFormat("[REGION SYNC CLIENT] HandleStartAnim for {0}", remoteClient.AgentId.ToString());
OSDMap data = new OSDMap(2);
data["agentID"] = OSD.FromUUID(remoteClient.AgentId);
data["id"] = OSD.FromUUID(animID);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.StartAnim, OSDParser.SerializeJsonString(data)));
}
public void HandleStopAnim(IClientAPI remoteClient, UUID animID)
{
// m_log.DebugFormat("[REGION SYNC CLIENT] HandleStopAnim for {0}", remoteClient.AgentId.ToString());
OSDMap data = new OSDMap(2);
data["agentID"] = OSD.FromUUID(remoteClient.AgentId);
data["id"] = OSD.FromUUID(animID);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.StopAnim, OSDParser.SerializeJsonString(data)));
}
/// <summary>
/// Convert a list of SurfaceTouchEventArgs into an OSDArray for transmission.
/// Return an array of maps which each are a list entry.
/// </summary>
/// <param name="surfaceArgs"></param>
/// <returns></returns>
private OSDArray MakeSurfaceArgsArray(List<SurfaceTouchEventArgs> surfaceArgs)
{
OSDArray surfaceList = new OSDArray();
for (int ii = 0; ii < surfaceArgs.Count; ii++)
{
OSDMap surfaceData = new OSDMap(6);
surfaceData["binormal"] = OSD.FromVector3(surfaceArgs[ii].Binormal);
surfaceData["faceIndex"] = OSD.FromInteger(surfaceArgs[ii].FaceIndex);
surfaceData["normal"] = OSD.FromVector3(surfaceArgs[ii].Normal);
surfaceData["position"] = OSD.FromVector3(surfaceArgs[ii].Position);
surfaceData["stCoord"] = OSD.FromVector3(surfaceArgs[ii].STCoord);
surfaceData["uvCoord"] = OSD.FromVector3(surfaceArgs[ii].UVCoord);
surfaceList.Add(surfaceData);
}
return surfaceList;
}
public void HandleChatFromClientRaw(object sender, byte[] 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
private void StatsTimerElapsed(object source, System.Timers.ElapsedEventArgs e)
{
// Let the auth sim know about this client's POV Re: Client counts
int t, l, r;
lock (m_syncRoot)
{
t = m_scene.SceneGraph.GetRootAgentCount();
l = LocalAvatars.Count;
r = RemoteAvatars.Count;
}
OSDMap data = new OSDMap(3);
data["total"] = OSD.FromInteger(t);
data["local"] = OSD.FromInteger(l);
data["remote"] = OSD.FromInteger(r);
//m_log.WarnFormat("{0}: Sent stats: {1},{2},{3}", LogHeader(), t, l, r);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.RegionStatus, OSDParser.SerializeJsonString(data)));
}
// Handles a message from the authoritative sim to balance client load by reducing client count to targetLoad
// by teleporting the extras to destinationRegion
private void BalanceClientLoad(int targetLoad, string destinationRegion)
{
int numberToTransfer = m_localAvatars.Count - targetLoad;
foreach(KeyValuePair<UUID,IClientAPI> kvp in LocalAvatars)
{
// BEGIN HACK FOR DEMO (Never teleport me please! I'll go where I please.)
if (kvp.Value.FirstName == "Dan" && kvp.Value.LastName == "Lake")
continue;
// END HACK FOR DEMO
ScenePresence sp;
m_scene.TryGetScenePresence(kvp.Key, out sp);
if (sp == null)
{
m_log.ErrorFormat("{0} Could not find scene presence {1} to teleport", LogHeader(), kvp.Value.Name);
continue;
}
sp.IsBalancing = true;
// Let the auth sim know we are losing responsibility for this client
OSDMap data = new OSDMap(1);
data["agentID"] = OSD.FromUUID(kvp.Key);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AvatarTeleportOut, OSDParser.SerializeJsonString(data)));
//kvp.Value.SendTeleportLocationStart();
m_scene.RequestTeleportLocation(kvp.Value, destinationRegion, sp.AbsolutePosition, sp.Lookat, (uint)TPFlags.ViaLocation);
if (--numberToTransfer <= 0)
break;
}
}
// When a client teleports into the region from another client manager, this function is called after
// the ScenePresence's RegionSyncAvatar client has been changed to an LLClientView.
// Here we will subscribe to interesting client events
public void IncomingLoadBalanceConnection(ScenePresence presence)
{
m_log.WarnFormat("{0} IncomingLoadBalanceConnection", LogHeader());
IClientAPI client = presence.ControllingClient;
// Let the auth sim know we just picked up responsibility for this client
OSDMap data = new OSDMap(1);
data["agentID"] = OSD.FromUUID(client.AgentId);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.AvatarTeleportIn, OSDParser.SerializeJsonString(data)));
lock (m_syncRoot)
{
Dictionary<UUID, IClientAPI> newlocals = new Dictionary<UUID, IClientAPI>(LocalAvatars);
newlocals.Add(client.AgentId, client);
m_localAvatars = newlocals;
Dictionary<UUID, RegionSyncAvatar> newremotes = new Dictionary<UUID, RegionSyncAvatar>(RemoteAvatars);
newremotes.Remove(client.AgentId);
m_remoteAvatars = newremotes;
}
// Register for interesting client events which will be forwarded to auth sim
// These are the raw packet data blocks from the client, intercepted and sent up to the sim
client.OnAgentUpdateRaw += HandleAgentUpdateRaw;
client.OnChatFromClientRaw += HandleChatFromClientRaw;
//KittyL: added to support same region teleport
client.OnSameRegionTeleportlRequest += HandleAgentSameRegionTeleport;
presence.IsSyncedAvatar = false;
}
}
}