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

536 lines
21 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.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Physics.Manager;
using log4net;
namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
{
//KittyL: NOTE -- We need to define an interface for all actors to connect into the Scene,
// e.g. IActorConnector, that runs on the Scene side, processes messages from actors,
// and apply Scene/Object operations.
// The SceneToPhysEngineConnector acts as a thread on the RegionSyncServer to handle incoming
// messages from PhysEngineToSceneConnectors that run on Physics Engines. It connects the
// authoratative Scene with remote script engines.
public class SceneToPhysEngineConnector : ISyncStatistics
{
#region SceneToPhysEngineConnector members
object stats = new object();
private DateTime lastStatTime;
private long msgsIn;
private long msgsOut;
private long bytesIn;
private long bytesOut;
private long pollBlocks;
private int msgCount = 0;
// The TcpClient this view uses to communicate with its RegionSyncClient
private TcpClient m_tcpclient;
// Set the addr and port for TcpListener
private IPAddress m_addr;
private Int32 m_port;
private static int m_connection_number = 0;
private Scene m_scene;
object m_syncRoot = new object();
Dictionary<UUID, RegionSyncAvatar> m_syncedAvatars = new Dictionary<UUID, RegionSyncAvatar>();
// A queue for incoming and outgoing traffic
private OpenMetaverse.BlockingQueue<RegionSyncMessage> inbox = new OpenMetaverse.BlockingQueue<RegionSyncMessage>();
private OpenMetaverse.BlockingQueue<RegionSyncMessage> outbox = new OpenMetaverse.BlockingQueue<RegionSyncMessage>();
private ILog m_log;
private Thread m_receive_loop;
private string m_regionName;
private SceneToPhysEngineSyncServer m_syncServer = null;
// A string of the format [REGION SYNC SCRIPT API (regionname)] for use in log headers
private string LogHeader
{
get
{
if (m_regionName == null)
return String.Format("[SceneToPhysEngineConnector #{0}]", m_connection_number);
return String.Format("[SceneToPhysEngineConnector #{0} ({1:10})]", m_connection_number, m_regionName);
}
}
// A string of the format "RegionSyncClientView #X" for use in describing the object itself
public string Description
{
get
{
if (m_regionName == null)
return String.Format("RegionSyncPhysAPI #{0}", m_connection_number);
return String.Format("RegionSyncPhysAPI #{0} ({1:10})", m_connection_number, m_regionName);
}
}
public int ConnectionNum
{
get { return m_connection_number; }
}
public string StatisticIdentifier()
{
return "SceneToPhysEngineConnector" + ConnectionNum.ToString();
}
public string StatisticLine(bool clearFlag)
{
string ret = "";
lock (stats)
{
double secondsSinceLastStats = DateTime.Now.Subtract(lastStatTime).TotalSeconds;
lastStatTime = DateTime.Now;
ret = String.Format("{0},{1},{2},{3},{4}",
msgsIn, msgsOut, bytesIn, bytesOut, pollBlocks
);
if (clearFlag)
msgsIn = msgsOut = bytesIn = bytesOut = pollBlocks = 0;
}
return ret;
}
public string StatisticTitle()
{
return "msgsIn,msgsOut,bytesIn,bytesOut,pollBlocks";
}
// Check if the client is connected
public bool Connected
{ get { return m_tcpclient.Connected; } }
#endregion
// Constructor
public SceneToPhysEngineConnector(int num, Scene scene, TcpClient client, SceneToPhysEngineSyncServer syncServer)
{
m_connection_number = num;
m_scene = scene;
m_tcpclient = client;
m_addr = ((IPEndPoint)client.Client.RemoteEndPoint).Address;
m_port = ((IPEndPoint)client.Client.RemoteEndPoint).Port;
m_syncServer = syncServer;
m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
//m_log.WarnFormat("{0} Constructed", LogHeader);
//Register for events from Scene.EventManager
//m_scene.EventManager.OnRezScript += SEConnectorOnRezScript;
//m_scene.EventManager.OnScriptReset += SEConnectorOnScriptReset;
//m_scene.EventManager.OnUpdateScript += SEConnectorOnUpdateScript;
// 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_receive_loop.Start();
SyncStatisticCollector.Register(this);
//tell the remote script engine about the locX, locY of this authoritative scene
// SendSceneLoc();
m_log.DebugFormat("{0}: SceneToPhysEngineConnector initialized", LogHeader);
}
// Stop the listening thread, disconnecting the RegionSyncPhysEngine
public void Shutdown()
{
m_syncServer.RemoveSyncedPhysEngine(this);
// m_scene.EventManager.OnChatFromClient -= EventManager_OnChatFromClient;
// Abort ReceiveLoop Thread, close Socket and TcpClient
m_receive_loop.Abort();
m_tcpclient.Client.Close();
m_tcpclient.Close();
//m_scene.EventManager.OnRezScript -= SEConnectorOnRezScript;
//m_scene.EventManager.OnScriptReset -= SEConnectorOnScriptReset;
//m_scene.EventManager.OnUpdateScript -= SEConnectorOnUpdateScript;
}
#region Send/Receive messages to/from the remote Physics Engine
// 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);
// Reset stats and time
lastStatTime = DateTime.Now;
msgsIn = msgsOut = bytesIn = bytesOut = 0;
try
{
while (true)
{
RegionSyncMessage msg = GetMessage();
lock (stats)
{
msgsIn++;
bytesIn += msg.Length;
}
lock (m_syncRoot)
HandleMessage(msg);
}
}
catch (Exception e)
{
m_log.WarnFormat("{0}: has disconnected: {1}", LogHeader, e.Message);
}
Shutdown();
}
// Get a message from the RegionSyncClient
private RegionSyncMessage GetMessage()
{
// Get a RegionSyncMessager from the incoming stream
RegionSyncMessage msg = new RegionSyncMessage(m_tcpclient.GetStream());
//m_log.WarnFormat("{0} Received {1}", LogHeader, msg.ToString());
return msg;
}
// Handle an incoming message
// *** Perhaps this should not be synchronous with the receive
// We could handle messages from an incoming Queue
private void HandleMessage(RegionSyncMessage msg)
{
SceneToPhysEngineSyncServer.PhysLogMessage(true, msg);
msgCount++;
//string handlerMessage = "";
switch (msg.Type)
{
case RegionSyncMessage.MsgType.ActorStop:
{
Shutdown();
}
return;
/*
case RegionSyncMessage.MsgType.LoadBalanceRequest:
{
m_syncServer.HandleLoadBalanceRequest(this);
return;
}
case RegionSyncMessage.MsgType.ActorStatus:
{
string status = Encoding.ASCII.GetString(msg.Data, 0, msg.Length);
ActorStatus actorStatus = (ActorStatus)Convert.ToInt32(status);
if (actorStatus == ActorStatus.Sync)
{
m_log.Debug(LogHeader + ": received ActorStatus " + actorStatus.ToString());
m_syncServer.AddSyncedPhysEngine(this);
}
else
{
m_log.Warn(LogHeader + ": not supposed to received RegionSyncMessage.MsgType.ActorStatus==" + status.ToString());
}
return;
}
*/
case RegionSyncMessage.MsgType.PhysTerseUpdate:
{
HandlePhysTerseUpdate(msg);
return;
}
case RegionSyncMessage.MsgType.PhysOutOfBounds:
{
HandlePhysOutOfBounds(msg);
return;
}
case RegionSyncMessage.MsgType.PhysCollisionUpdate:
{
HandlePhysCollisionUpdate(msg);
return;
}
case RegionSyncMessage.MsgType.PhysUpdateAttributes:
{
HandlePhysUpdateAttributes(msg);
return;
}
default:
{
m_log.WarnFormat("{0} Unable to handle unsupported message type", LogHeader);
return;
}
}
}
private void HandlePhysTerseUpdate(RegionSyncMessage msg)
{
OSDMap data = RegionSyncUtil.DeserializeMessage(msg, LogHeader);
try
{
UUID uuid = data["uuid"].AsUUID();
// m_log.DebugFormat("{0}: received PhysUpdateAttributes for {1}", LogHeader, uuid);
PhysicsActor pa = FindPhysicsActor(uuid);
if (pa != null)
{
pa.RequestPhysicsterseUpdate();
}
else
{
m_log.WarnFormat("{0}: terse update for unknown uuid {1}", LogHeader, uuid);
return;
}
}
catch (Exception e)
{
m_log.WarnFormat("{0}: EXCEPTION processing PhysTerseUpdate: {1}", LogHeader, e);
return;
}
return;
}
private void HandlePhysOutOfBounds(RegionSyncMessage msg)
{
// TODO:
return;
}
private void HandlePhysCollisionUpdate(RegionSyncMessage msg)
{
// TODO:
return;
}
/// <summary>
/// The physics engine has some updates to the attributes. Unpack the parameters, find the
/// correct PhysicsActor and plug in the new values;
/// </summary>
/// <param name="msg"></param>
private void HandlePhysUpdateAttributes(RegionSyncMessage msg)
{
OSDMap data = RegionSyncUtil.DeserializeMessage(msg, LogHeader);
try
{
UUID uuid = data["uuid"].AsUUID();
string actorID = data["actorID"].AsString();
// m_log.DebugFormat("{0}: received PhysUpdateAttributes for {1}", LogHeader, uuid);
PhysicsActor pa = FindPhysicsActor(uuid);
if (pa != null)
{
if (pa.PhysicsActorType == (int)ActorTypes.Prim)
{
m_log.WarnFormat("{0}: HandlePhysUpdateAttributes for an prim: {1}", LogHeader, pa.UUID);
}
pa.Size = data["size"].AsVector3();
pa.Position = data["position"].AsVector3();
pa.Force = data["force"].AsVector3();
pa.Velocity = data["velocity"].AsVector3();
pa.RotationalVelocity = data["rotationalVelocity"].AsVector3();
pa.Acceleration = data["acceleration"].AsVector3();
pa.Torque = data["torque"].AsVector3();
pa.Orientation = data["orientation"].AsQuaternion();
pa.IsPhysical = data["isPhysical"].AsBoolean(); // receive??
pa.Flying = data["flying"].AsBoolean(); // receive??
pa.Kinematic = data["kinematic"].AsBoolean(); // receive??
pa.Buoyancy = (float)(data["buoyancy"].AsReal());
pa.CollidingGround = data["isCollidingGround"].AsBoolean();
pa.IsColliding = data["isColliding"].AsBoolean();
pa.ChangingActorID = actorID;
pa.RequestPhysicsterseUpdate(); // tell the system the values have changed
}
else
{
m_log.WarnFormat("{0}: attribute update for unknown uuid {1}", LogHeader, uuid);
return;
}
}
catch (Exception e)
{
m_log.WarnFormat("{0}: EXCEPTION processing UpdateAttributes: {1}", LogHeader, e);
return;
}
return;
}
// Find the physics actor whether it is an object or a scene presence
private PhysicsActor FindPhysicsActor(UUID uuid)
{
SceneObjectPart sop = m_scene.GetSceneObjectPart(uuid);
if (sop != null)
{
return sop.PhysActor;
}
ScenePresence sp = m_scene.GetScenePresence(uuid);
if (sp != null)
{
return sp.PhysicsActor;
}
return null;
}
public void SendPhysUpdateAttributes(PhysicsActor pa)
{
// m_log.DebugFormat("{0}: sending PhysUpdateAttributes for {1}", LogHeader, pa.UUID);
if (pa.PhysicsActorType == (int)ActorTypes.Prim)
{
m_log.WarnFormat("{0}: SendPhysUpdateAttributes for an prim: {1}", LogHeader, pa.UUID);
}
OSDMap data = new OSDMap(15);
data["time"] = OSD.FromString(DateTime.Now.ToString("yyyyMMddHHmmssfff"));
data["localID"] = OSD.FromUInteger(pa.LocalID);
data["uuid"] = OSD.FromUUID(pa.UUID);
data["actorID"] = OSD.FromString(RegionSyncServerModule.ActorID);
data["size"] = OSD.FromVector3(pa.Size);
data["position"] = OSD.FromVector3(pa.Position);
data["force"] = OSD.FromVector3(pa.Force);
data["velocity"] = OSD.FromVector3(pa.Velocity);
data["rotationalVelocity"] = OSD.FromVector3(pa.RotationalVelocity);
data["acceleration"] = OSD.FromVector3(pa.Acceleration);
data["torque"] = OSD.FromVector3(pa.Torque);
data["orientation"] = OSD.FromQuaternion(pa.Orientation);
data["isPhysical"] = OSD.FromBoolean(pa.IsPhysical);
data["flying"] = OSD.FromBoolean(pa.Flying);
data["buoyancy"] = OSD.FromReal(pa.Buoyancy);
data["isColliding"] = OSD.FromBoolean(pa.IsColliding);
data["isCollidingGround"] = OSD.FromBoolean(pa.CollidingGround);
//m_log.DebugFormat("SendPhysUpdateAttributes, ChangingActorID = {0}, with PA at pos {1}, data.position={2}", pa.ChangingActorID, pa.Position, data["position"].AsVector3().ToString());
RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.PhysUpdateAttributes,
OSDParser.SerializeJsonString(data));
Send(rsm);
return;
}
private RegionSyncMessage PrepareObjectUpdateMessage(RegionSyncMessage.MsgType msgType, SceneObjectGroup sog)
{
OSDMap data = new OSDMap(3);
data["locX"] = OSD.FromUInteger(m_scene.RegionInfo.RegionLocX);
data["locY"] = OSD.FromUInteger(m_scene.RegionInfo.RegionLocY);
string sogxml = SceneObjectSerializer.ToXml2Format(sog);
data["sogXml"] = OSD.FromString(sogxml);
RegionSyncMessage rsm = new RegionSyncMessage(msgType, OSDParser.SerializeJsonString(data));
return rsm;
}
private void SendSceneLoc()
{
uint locX = m_scene.RegionInfo.RegionLocX;
uint locY = m_scene.RegionInfo.RegionLocY;
OSDMap data = new OSDMap(2);
data["locX"] = OSD.FromUInteger(locX);
data["locY"] = OSD.FromUInteger(locY);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.SceneLocation, OSDParser.SerializeJsonString(data)));
}
public void Send(RegionSyncMessage msg)
{
if (msg.Type == RegionSyncMessage.MsgType.AvatarAppearance)
m_log.WarnFormat("{0} Sending AvatarAppearance to client manager", LogHeader);
Send(msg.ToBytes());
}
private void Send(byte[] data)
{
if (m_tcpclient.Connected)
{
try
{
lock (stats)
{
msgsOut++;
bytesOut += data.Length;
}
m_tcpclient.GetStream().BeginWrite(data, 0, data.Length, ar =>
{
if (m_tcpclient.Connected)
{
try
{
m_tcpclient.GetStream().EndWrite(ar);
}
catch (Exception)
{
m_log.WarnFormat("{0} Write to output stream failed", LogHeader);
}
}
}, null);
}
catch (IOException)
{
m_log.WarnFormat("{0} Physics Engine has disconnected.", LogHeader);
}
}
else
{
m_log.DebugFormat("{0} Attempt to send with no connection", LogHeader);
}
}
public void SendObjectUpdate(RegionSyncMessage.MsgType msgType, SceneObjectGroup sog)
{
Send(PrepareObjectUpdateMessage(msgType, sog));
}
#endregion Send/Receive messages to/from the remote Physics Engine
#region Load balancing functions
/*
public void SendLoadBalanceRejection(string response)
{
RegionSyncMessage msg = new RegionSyncMessage(RegionSyncMessage.MsgType.LoadBalanceRejection, response);
Send(msg);
}
*/
#endregion
}
}