Initial plumbing for connectors and PhysActor discovery
parent
9e68de6b8f
commit
b56b0c3086
|
@ -0,0 +1,583 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Services.Interfaces;
|
||||
using OpenSim.Framework.Client;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes.Types;
|
||||
using log4net;
|
||||
|
||||
using Nini.Config;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
|
||||
{
|
||||
//The data structure that maintains the list of quarks a script engine subscribes to.
|
||||
//It might be better to organize the quarks in a k-d tree structure, for easier
|
||||
//partitioning of the quarks based on spatial information.
|
||||
//But for now, we just assume the quarks each script engine operates on form a rectangle shape.
|
||||
//So we just use xmin,ymin and xmax,ymax to identify the rectange; and use a List structure to
|
||||
//store the quarks.
|
||||
//Quark size is defined in QuarkInfo.SizeX and QuarkInfo.SizeY.
|
||||
|
||||
// The RegionSyncPhysEngine has a receive thread to process messages from the RegionSyncServer.
|
||||
// It is the client side of the synchronization channel, and send to and receive updates from the
|
||||
// Auth. Scene. The server side thread handling the sync channel is implemented in RegionSyncScriptAPI.cs.
|
||||
//
|
||||
// The current implementation is very similar to RegionSyncClient.
|
||||
// TODO: eventually the same RegionSyncSceneAPI should handle all traffic from different actors, e.g.
|
||||
// using a pub/sub model.
|
||||
public class PhysEngineToSceneConnector
|
||||
{
|
||||
#region PhysEngineToSceneConnector members
|
||||
|
||||
// Set the addr and port of RegionSyncServer
|
||||
private IPAddress m_addr;
|
||||
private string m_addrString;
|
||||
private Int32 m_port;
|
||||
|
||||
// A reference to the local scene
|
||||
private Scene m_validLocalScene;
|
||||
|
||||
// The avatars added to this client manager for clients on other client managers
|
||||
object m_syncRoot = new object();
|
||||
|
||||
// The logfile
|
||||
private ILog m_log;
|
||||
|
||||
private string LogHeader = "[PHYSICS ENGINE TO SCENE CONNECTOR]";
|
||||
|
||||
// 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_authSceneName;
|
||||
|
||||
//KittyL: Comment out m_statsTimer for now, will figure out whether we need it for PhysEngine later
|
||||
//private System.Timers.Timer m_statsTimer = new System.Timers.Timer(30000);
|
||||
|
||||
// The queue of incoming messages which need handling
|
||||
//private Queue<string> m_inQ = new Queue<string>();
|
||||
|
||||
//KittyL: added to identify different actors
|
||||
private ActorType m_actorType = ActorType.PhysicsEngine;
|
||||
|
||||
private bool m_debugWithViewer = false;
|
||||
|
||||
private QuarkSubsriptionInfo m_subscribedQuarks;
|
||||
|
||||
|
||||
private IConfig m_sysConfig;
|
||||
|
||||
//members for load balancing purpose
|
||||
//private TcpClient m_loadMigrationSouceEnd = null;
|
||||
private LoadMigrationEndPoint m_loadMigrationSouceEnd = null;
|
||||
private Thread m_loadMigrationSrcRcvLoop;
|
||||
private LoadMigrationListener m_loadMigrationListener = null;
|
||||
|
||||
//List of queued messages, when the space that the updated object is located is being migrated
|
||||
private List<RegionSyncMessage> m_updateMsgQueue = new List<RegionSyncMessage>();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
// Constructor
|
||||
public PhysEngineToSceneConnector(Scene validLocalScene, string addr, int port, bool debugWithViewer, string authSceneName, IConfig sysConfig)
|
||||
{
|
||||
m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
m_validLocalScene = validLocalScene;
|
||||
m_addr = IPAddress.Parse(addr);
|
||||
m_addrString = addr;
|
||||
m_port = port;
|
||||
m_debugWithViewer = debugWithViewer;
|
||||
m_authSceneName = authSceneName;
|
||||
//m_statsTimer.Elapsed += new System.Timers.ElapsedEventHandler(StatsTimerElapsed);
|
||||
m_sysConfig = sysConfig;
|
||||
|
||||
//assume we are connecting to the whole scene as one big quark
|
||||
m_subscribedQuarks = new QuarkSubsriptionInfo(0, 0, (int)Constants.RegionSize, (int)Constants.RegionSize);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a PhysEngineToSceneConnector based on the space it is supposed to subscribe (and operate) on.
|
||||
/// </summary>
|
||||
/// <param name="validLocalScene"></param>
|
||||
/// <param name="addr"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="debugWithViewer"></param>
|
||||
/// <param name="authSceneName"></param>
|
||||
/// <param name="subscriptionSpace"></param>
|
||||
/// <param name="quarkSizeX"></param>
|
||||
/// <param name="quarkSizeY"></param>
|
||||
/// <param name="sysConfig"></param>
|
||||
public PhysEngineToSceneConnector(Scene validLocalScene, string addr, int port, bool debugWithViewer, string authSceneName,
|
||||
string subscriptionSpace, IConfig sysConfig)
|
||||
{
|
||||
if (QuarkInfo.SizeX == -1 || QuarkInfo.SizeY == -1)
|
||||
{
|
||||
m_log.Error("QuarkInfo.SizeX or QuarkInfo.SizeY has not been configured.");
|
||||
Environment.Exit(0);
|
||||
}
|
||||
|
||||
m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
m_validLocalScene = validLocalScene;
|
||||
m_addr = IPAddress.Parse(addr);
|
||||
m_addrString = addr;
|
||||
m_port = port;
|
||||
m_debugWithViewer = debugWithViewer;
|
||||
m_authSceneName = authSceneName;
|
||||
//m_statsTimer.Elapsed += new System.Timers.ElapsedEventHandler(StatsTimerElapsed);
|
||||
m_sysConfig = sysConfig;
|
||||
|
||||
m_subscribedQuarks = new QuarkSubsriptionInfo(subscriptionSpace);
|
||||
}
|
||||
|
||||
public PhysEngineToSceneConnectorModule GetPEToSceneConnectorMasterModule()
|
||||
{
|
||||
if (m_validLocalScene == null)
|
||||
return null;
|
||||
return (PhysEngineToSceneConnectorModule)m_validLocalScene.PhysEngineToSceneConnectorModule;
|
||||
}
|
||||
|
||||
public Scene GetValidLocalScene()
|
||||
{
|
||||
return m_validLocalScene;
|
||||
}
|
||||
|
||||
|
||||
private List<string> GetQuarkStringList()
|
||||
{
|
||||
List<string> quarkList = new List<string>();
|
||||
foreach (QuarkInfo quark in m_subscribedQuarks.QuarkList)
|
||||
{
|
||||
quarkList.Add(quark.QuarkStringRepresentation);
|
||||
}
|
||||
return quarkList;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get the reference to the local scene that is supposed to be mapped to the remote auth. scene.
|
||||
/// </summary>
|
||||
/// <param name="authSceneName"></param>
|
||||
/// <returns></returns>
|
||||
private Scene GetLocalScene(string authSceneName)
|
||||
{
|
||||
PhysEngineToSceneConnectorModule connectorModule = GetPEToSceneConnectorMasterModule();
|
||||
return connectorModule.GetLocalScene(authSceneName);
|
||||
}
|
||||
|
||||
// Start the RegionSyncPhysEngine client thread
|
||||
public bool Start()
|
||||
{
|
||||
if (EstablishConnection())
|
||||
{
|
||||
StartStateSync();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private bool EstablishConnection()
|
||||
{
|
||||
if (m_client.Connected)
|
||||
{
|
||||
m_log.Warn(LogHeader + ": already connected");
|
||||
return false;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_client.Connect(m_addr, m_port);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.WarnFormat("{0} [Start] Could not connect to SceneToPhysEngineSyncServer at {1}:{2}", LogHeader, m_addr, m_port);
|
||||
m_log.Warn(e.Message);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_log.WarnFormat("{0} Connected to SceneToPhysEngineSyncServer at {1}:{2}", LogHeader, m_addr, m_port);
|
||||
|
||||
m_rcvLoop = new Thread(new ThreadStart(ReceiveLoop));
|
||||
m_rcvLoop.Name = "PhysEngineToSceneConnector ReceiveLoop";
|
||||
m_log.WarnFormat("{0} Starting {1} thread", LogHeader, m_rcvLoop.Name);
|
||||
m_rcvLoop.Start();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void StartStateSync()
|
||||
{
|
||||
RegionSyncMessage msg = new RegionSyncMessage(RegionSyncMessage.MsgType.ActorStatus, Convert.ToString((int)ActorStatus.Sync));
|
||||
Send(msg);
|
||||
SendQuarkSubscription();
|
||||
Thread.Sleep(100);
|
||||
DoInitialSync();
|
||||
}
|
||||
|
||||
|
||||
private void SendQuarkSubscription()
|
||||
{
|
||||
List<string> quarkStringList = GetQuarkStringList();
|
||||
string quarkString = RegionSyncUtil.QuarkStringListToString(quarkStringList);
|
||||
|
||||
m_log.Debug(LogHeader + ": subscribe to quarks: " + quarkString);
|
||||
//Send(quarkString);
|
||||
RegionSyncMessage msg = new RegionSyncMessage(RegionSyncMessage.MsgType.QuarkSubscription, quarkString);
|
||||
Send(msg);
|
||||
}
|
||||
|
||||
public void SetQuarkSubscription(QuarkSubsriptionInfo quarks)
|
||||
{
|
||||
m_subscribedQuarks = quarks;
|
||||
}
|
||||
|
||||
public void RegisterIdle()
|
||||
{
|
||||
EstablishConnection();
|
||||
RegionSyncMessage msg = new RegionSyncMessage(RegionSyncMessage.MsgType.ActorStatus, Convert.ToString((int)ActorStatus.Idle));
|
||||
Send(msg);
|
||||
}
|
||||
|
||||
private void DoInitialSync()
|
||||
{
|
||||
m_validLocalScene.DeleteAllSceneObjects();
|
||||
//m_log.Debug(LogHeader + ": send actor type " + m_actorType);
|
||||
//Send(new RegionSyncMessage(RegionSyncMessage.MsgType.ActorType, Convert.ToString((int)m_actorType)));
|
||||
//KittyL??? Do we need to send in RegionName?
|
||||
|
||||
//Send(new RegionSyncMessage(RegionSyncMessage.MsgType.RegionName, m_scene.RegionInfo.RegionName));
|
||||
//m_log.WarnFormat("Sending region name: \"{0}\"", m_scene.RegionInfo.RegionName);
|
||||
|
||||
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.GetTerrain));
|
||||
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.GetObjects));
|
||||
|
||||
// Register for events which will be forwarded to authoritative scene
|
||||
// m_scene.EventManager.OnNewClient += EventManager_OnNewClient;
|
||||
//m_scene.EventManager.OnClientClosed += new EventManager.ClientClosed(RemoveLocalClient);
|
||||
}
|
||||
|
||||
// Disconnect from the RegionSyncServer and close client thread
|
||||
public void Stop()
|
||||
{
|
||||
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.ActorStop, "stop"));
|
||||
// The remote scene will remove the SceneToPhysEngineConnector when we disconnect
|
||||
m_rcvLoop.Abort();
|
||||
ShutdownClient();
|
||||
|
||||
//stop the migration connections
|
||||
//ShutdownClient(m_loadMigrationSouceEnd);
|
||||
if (m_loadMigrationListener != null)
|
||||
m_loadMigrationListener.Shutdown();
|
||||
}
|
||||
|
||||
public void ReportStatus()
|
||||
{
|
||||
m_log.WarnFormat("{0} Synchronized to RegionSyncServer at {1}:{2}", LogHeader, m_addr, m_port);
|
||||
lock (m_syncRoot)
|
||||
{
|
||||
//TODO: should be reporting about the information of the objects/scripts
|
||||
}
|
||||
}
|
||||
|
||||
private void ShutdownClient()
|
||||
{
|
||||
m_log.WarnFormat("{0} Disconnected from RegionSyncServer. Shutting down.", LogHeader);
|
||||
|
||||
//TODO: remove the objects and scripts
|
||||
//lock (m_syncRoot)
|
||||
//{
|
||||
|
||||
//}
|
||||
|
||||
if (m_client != null)
|
||||
{
|
||||
// Close the connection
|
||||
m_client.Client.Close();
|
||||
m_client.Close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Listen for messages from a RegionSyncServer
|
||||
// *** 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
|
||||
{
|
||||
//lock (m_syncRoot) -- KittyL: why do we need to lock here? We could lock inside HandleMessage if necessary, and lock on different objects for better performance
|
||||
HandleMessage(msg);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.WarnFormat("{0} Encountered an exception: {1} (MSGTYPE = {2})", LogHeader, e.Message, msg.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region SEND
|
||||
//DSG-TODO: for Scene based DSG, Send() also needs to figure out which Scene to send to, e.g. needs a switching function based on object position
|
||||
|
||||
// Send a message to a single connected RegionSyncServer
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send requests to update object properties in the remote authoratative Scene.
|
||||
/// </summary>
|
||||
/// <param name="primID">UUID of the object</param>
|
||||
/// <param name="pName">name of the property to be updated</param>
|
||||
/// <param name="valParams">parameters of the value of the property</param>
|
||||
/// <returns></returns>
|
||||
public void SendSetPrimProperties(UUID primID, string pName, object val)
|
||||
{
|
||||
OSDMap data = new OSDMap();
|
||||
data["UUID"] = OSD.FromUUID(primID);
|
||||
data["name"] = OSD.FromString(pName);
|
||||
object[] valParams = (object[])val;
|
||||
//data["param"] = OSD.FromString(presence.ControllingClient.LastName);
|
||||
Vector3 pos, vel;
|
||||
switch (pName)
|
||||
{
|
||||
case "object_rez":
|
||||
//this is to rez an object from the prim's inventory, rather than change the prim's property
|
||||
if(valParams.Length<5){
|
||||
m_log.Warn(LogHeader+": values for object's "+pName+" property should include: inventory, pos, velocity, rotation, param");
|
||||
return;
|
||||
}
|
||||
string inventory = (string)valParams[0];
|
||||
pos = (Vector3)valParams[1];
|
||||
vel = (Vector3)valParams[2];
|
||||
Quaternion rot = (Quaternion)valParams[3];
|
||||
int param = (int)valParams[4];
|
||||
data["inventory"]=OSD.FromString(inventory);
|
||||
data["pos"]=OSD.FromVector3(pos);
|
||||
data["vel"] = OSD.FromVector3(vel);
|
||||
data["rot"] = OSD.FromQuaternion(rot);
|
||||
data["param"] = OSD.FromInteger(param);
|
||||
break;
|
||||
case "color":
|
||||
if(valParams.Length<2){
|
||||
m_log.Warn(LogHeader+": values for object's "+pName+" property should include: color-x, color-y, color-z, face");
|
||||
return;
|
||||
}
|
||||
//double cx = (double)valParams[0];
|
||||
//double cy = (double)valParams[1];
|
||||
//double cz = (double)valParams[2];
|
||||
//Vector3 color = new Vector3((float)cx, (float)cy, (float)cz);
|
||||
Vector3 color = (Vector3)valParams[0];
|
||||
data["color"] = OSD.FromVector3(color);
|
||||
data["face"] = OSD.FromInteger((int)valParams[1]);
|
||||
|
||||
//m_log.DebugFormat("{0}: to set color {1} on face {2} of prim {3}", LogHeader, color.ToString(), (int)valParams[1], primID);
|
||||
|
||||
break;
|
||||
case "pos":
|
||||
if (valParams.Length < 1)
|
||||
{
|
||||
m_log.Warn(LogHeader + ": values for object's " + pName + " property should include: pos(vector)");
|
||||
return;
|
||||
}
|
||||
//double px = (double)valParams[0];
|
||||
//double py = (double)valParams[1];
|
||||
//double pz = (double)valParams[2];
|
||||
//Vector3 pos = new Vector3((float)px, (float)py, (float)pz);
|
||||
pos = (Vector3)valParams[0];
|
||||
data["pos"] = OSD.FromVector3(pos);
|
||||
|
||||
m_log.DebugFormat("{0}: to set pos {1} for prim {2}", LogHeader, pos.ToString(), primID);
|
||||
break;
|
||||
default:
|
||||
//
|
||||
break;
|
||||
}
|
||||
|
||||
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.SetObjectProperty, OSDParser.SerializeJsonString(data)));
|
||||
}
|
||||
#endregion SEND
|
||||
|
||||
//KittyL: Has to define SendCoarseLocations() here, since it's defined in IRegionSyncClientModule.
|
||||
// But should not do much as being PhysEngine, not ClientManager
|
||||
public void SendCoarseLocations()
|
||||
{
|
||||
}
|
||||
|
||||
// Handle an incoming message
|
||||
// Dan-TODO: This should not be synchronous with the receive!
|
||||
// Instead, handle messages from an incoming Queue so server doesn't block sending
|
||||
//
|
||||
// KittyL: This is the function that PhysEngine and ClientManager have the most different implementations
|
||||
private void HandleMessage(RegionSyncMessage msg)
|
||||
{
|
||||
//TO FINISH:
|
||||
|
||||
switch (msg.Type)
|
||||
{
|
||||
case RegionSyncMessage.MsgType.RegionName:
|
||||
{
|
||||
string authSceneName = Encoding.ASCII.GetString(msg.Data, 0, msg.Length);
|
||||
if (authSceneName != m_authSceneName)
|
||||
{
|
||||
//This should not happen. If happens, check the configuration files (OpenSim.ini) on other sides.
|
||||
m_log.Warn(": !!! Mismatch between configurations of authoritative scene. Script Engine's config: "+m_authSceneName+", Scene's config: "+authSceneName);
|
||||
return;
|
||||
}
|
||||
RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Syncing to region \"{0}\"", m_authSceneName));
|
||||
return;
|
||||
}
|
||||
case RegionSyncMessage.MsgType.Terrain:
|
||||
{
|
||||
//We need to handle terrain differently as we handle objects: we really will set the HeightMap
|
||||
//of each local scene that is the shadow copy of its auth. scene.
|
||||
Scene localScene = GetLocalScene(m_authSceneName);
|
||||
if (localScene == null)
|
||||
{
|
||||
m_log.Warn("no local Scene mapped to "+m_authSceneName);
|
||||
return;
|
||||
}
|
||||
localScene.Heightmap.LoadFromXmlString(Encoding.ASCII.GetString(msg.Data, 0, msg.Length));
|
||||
RegionSyncMessage.HandleSuccess(LogHeader, msg, "Synchronized terrain");
|
||||
return;
|
||||
}
|
||||
case RegionSyncMessage.MsgType.NewObject:
|
||||
case RegionSyncMessage.MsgType.UpdatedObject:
|
||||
{
|
||||
HandleAddOrUpdateObjectInLocalScene(msg);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{
|
||||
RegionSyncMessage.HandleError(LogHeader, msg, String.Format("{0} Unsupported message type: {1}", LogHeader, ((int)msg.Type).ToString()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#region Utility functions
|
||||
|
||||
private OSDMap GetOSDMap(string strdata)
|
||||
{
|
||||
OSDMap args = null;
|
||||
OSD buffer = OSDParser.DeserializeJson(strdata);
|
||||
if (buffer.Type == OSDType.Map)
|
||||
{
|
||||
args = (OSDMap)buffer;
|
||||
return args;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public string GetServerAddressAndPort()
|
||||
{
|
||||
return m_addr.ToString() + ":" + m_port.ToString();
|
||||
}
|
||||
|
||||
#endregion Utility functions
|
||||
|
||||
#region Handlers for Scene events
|
||||
|
||||
private void HandleAddOrUpdateObjectInLocalScene(RegionSyncMessage msg)
|
||||
{
|
||||
// TODO: modify for physics
|
||||
OSDMap data = DeserializeMessage(msg);
|
||||
/*
|
||||
if (data["locX"] == null || data["locY"] == null || data["sogXml"] == null)
|
||||
{
|
||||
m_log.Warn(LogHeader + ": parameters missing in NewObject/UpdatedObject message, need to have locX, locY, sogXml");
|
||||
return;
|
||||
}
|
||||
* */
|
||||
uint locX = data["locX"].AsUInteger();
|
||||
uint locY = data["locY"].AsUInteger();
|
||||
string sogxml = data["sogXml"].AsString();
|
||||
SceneObjectGroup sog = SceneObjectSerializer.FromXml2Format(sogxml);
|
||||
|
||||
}
|
||||
|
||||
#endregion Handlers for events/updates from Scene
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,796 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSim 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.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using Nini.Config;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Client;
|
||||
using OpenSim.Region.CoreModules.Framework.InterfaceCommander;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using log4net;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
|
||||
{
|
||||
//The connector that connects the local Scene (cache) and remote authoratative Scene
|
||||
public class PhysEngineToSceneConnectorModule : IRegionModule, IPhysEngineToSceneConnectorModule, ICommandableModule
|
||||
{
|
||||
#region IRegionModule Members
|
||||
|
||||
public void Initialise(Scene scene, IConfigSource config)
|
||||
{
|
||||
m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
m_active = false; //set to false unless this is the valid local scene
|
||||
|
||||
//Read in configuration
|
||||
IConfig syncConfig = config.Configs["RegionSyncModule"];
|
||||
if (syncConfig != null && syncConfig.GetString("Enabled", "").ToLower() == "true")
|
||||
{
|
||||
scene.RegionSyncEnabled = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
scene.RegionSyncEnabled = false;
|
||||
}
|
||||
|
||||
m_regionSyncMode = syncConfig.GetString("Mode", "").ToLower();
|
||||
if (syncConfig == null || m_regionSyncMode != "physics_engine")
|
||||
{
|
||||
m_log.Warn("[REGION SYNC PHYSICS ENGINE MODULE] Not in script_engine mode. Shutting down.");
|
||||
return;
|
||||
}
|
||||
|
||||
//get the name of the valid region for script engine, i.e., that region that will holds all objects and scripts
|
||||
//if not matching m_scene's name, simply return
|
||||
string validLocalScene = syncConfig.GetString("ValidPhysEngineScene", "");
|
||||
if (!validLocalScene.Equals(scene.RegionInfo.RegionName))
|
||||
{
|
||||
m_log.Warn("Not the valid local scene, shutting down");
|
||||
return;
|
||||
}
|
||||
m_active = true;
|
||||
m_validLocalScene = validLocalScene;
|
||||
|
||||
m_log.Debug("Init PEToSceneConnectorModule, for local scene " + scene.RegionInfo.RegionName);
|
||||
|
||||
//get the number of regions this script engine subscribes
|
||||
m_sceneNum = syncConfig.GetInt("SceneNumber", 1);
|
||||
|
||||
//get the mapping of local scenes to auth. scenes
|
||||
List<string> authScenes = new List<string>();
|
||||
for (int i = 0; i < m_sceneNum; i++)
|
||||
{
|
||||
string localScene = "LocalScene" + i;
|
||||
string localSceneName = syncConfig.GetString(localScene, "");
|
||||
string masterScene = localScene + "Master";
|
||||
string masterSceneName = syncConfig.GetString(masterScene, "");
|
||||
|
||||
if (localSceneName.Equals("") || masterSceneName.Equals(""))
|
||||
{
|
||||
m_log.Warn(localScene + " or " + masterScene+ " has not been assigned a value in configuration. Shutting down.");
|
||||
return;
|
||||
}
|
||||
|
||||
//m_localToAuthSceneMapping.Add(localSceneName, masterSceneName);
|
||||
RecordLocalAuthSceneMappings(localSceneName, masterSceneName);
|
||||
authScenes.Add(masterSceneName);
|
||||
m_localScenesByName.Add(localSceneName, null);
|
||||
}
|
||||
|
||||
int defaultPort = 13000;
|
||||
//get the addr:port info of the authoritative scenes
|
||||
for (int i = 0; i < m_sceneNum; i++)
|
||||
{
|
||||
string authSceneName = authScenes[i];
|
||||
//string serverAddr = authSceneName + "_ServerIPAddress";
|
||||
//string serverPort = authSceneName + "_ServerPort";
|
||||
string serverAddr = authSceneName + "_SceneToPESyncServerIP";
|
||||
string addr = syncConfig.GetString(serverAddr, "127.0.0.1");
|
||||
string serverPort = authSceneName + "_SceneToPESyncServerPort";
|
||||
int port = syncConfig.GetInt(serverPort, defaultPort);
|
||||
defaultPort++;
|
||||
|
||||
AuthSceneInfo authSceneInfo = new AuthSceneInfo(authSceneName, addr, port);
|
||||
m_authScenesInfoByName.Add(authSceneName, authSceneInfo);
|
||||
}
|
||||
|
||||
m_scene = scene;
|
||||
m_scene.RegisterModuleInterface<IPhysEngineToSceneConnectorModule>(this);
|
||||
m_syncConfig = syncConfig;
|
||||
m_debugWithViewer = syncConfig.GetBoolean("PhysEngineDebugWithViewer", false);
|
||||
|
||||
//read in the quark size information
|
||||
//QuarkInfo.SizeX = syncConfig.GetInt("QuarkSizeX", (int)Constants.RegionSize);
|
||||
//QuarkInfo.SizeY = syncConfig.GetInt("QuarkSizeY", (int)Constants.RegionSize);
|
||||
QuarkInfo.SizeX = syncConfig.GetInt("QuarkSizeX", (int)Constants.RegionSize);
|
||||
QuarkInfo.SizeY = syncConfig.GetInt("QuarkSizeY", (int)Constants.RegionSize);
|
||||
|
||||
//m_quarkListString = syncConfig.GetString("InitQuarkSet", ""); //if not specified, dost not subscribe to any quark
|
||||
//if (m_quarkListString.Equals("all"))
|
||||
//{
|
||||
// m_quarkListString = RegionSyncUtil.QuarkStringListToString(RegionSyncUtil.GetAllQuarkStringInScene(QuarkInfo.SizeX, QuarkInfo.SizeY));
|
||||
//}
|
||||
m_subscriptionSpaceString = syncConfig.GetString("InitSubscriptionSpace", "0_0,256_256");
|
||||
|
||||
|
||||
// Setup the command line interface
|
||||
m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole;
|
||||
InstallInterfaces();
|
||||
|
||||
m_log.Warn("[REGION SYNC PHYSICS ENGINE MODULE] Initialised");
|
||||
}
|
||||
|
||||
public void PostInitialise()
|
||||
{
|
||||
if (!m_active)
|
||||
return;
|
||||
|
||||
//m_log.Warn("[REGION SYNC CLIENT MODULE] Post-Initialised");
|
||||
m_scene.EventManager.OnPopulateLocalSceneList += OnPopulateLocalSceneList;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
if (m_active)
|
||||
{
|
||||
m_scene.EventManager.OnPopulateLocalSceneList -= OnPopulateLocalSceneList;
|
||||
}
|
||||
m_scene = null;
|
||||
m_active = false;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return "RegionSyncPhysEngineModule"; }
|
||||
}
|
||||
|
||||
public bool IsSharedModule
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ICommandableModule Members
|
||||
private readonly Commander m_commander = new Commander("sync");
|
||||
public ICommander CommandInterface
|
||||
{
|
||||
get { return m_commander; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IPhysEngineToSceneConnectorModule members
|
||||
|
||||
|
||||
public bool Active
|
||||
{
|
||||
get { return m_active; }
|
||||
}
|
||||
|
||||
public bool Synced
|
||||
{
|
||||
get
|
||||
{
|
||||
lock(m_client_lock)
|
||||
{
|
||||
return (m_PEToSceneConnectors.Count > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region PhysEngineToSceneConnectorModule members and functions
|
||||
|
||||
private bool m_active = false;
|
||||
private string m_serveraddr;
|
||||
private int m_serverport;
|
||||
private Scene m_scene;
|
||||
private ILog m_log;
|
||||
private Object m_client_lock = new Object();
|
||||
//private PhysEngineToSceneConnector m_scriptEngineToSceneConnector = null;
|
||||
private IConfig m_syncConfig = null;
|
||||
private bool m_debugWithViewer = false;
|
||||
private string m_regionSyncMode = "";
|
||||
|
||||
//Variables relavant for multi-scene subscription.
|
||||
private int m_sceneNum = 0;
|
||||
private string m_validLocalScene = "";
|
||||
private Dictionary<string, string> m_localToAuthSceneMapping = new Dictionary<string,string>(); //1-1 mapping from local shadow scene to authoratative scene
|
||||
private Dictionary<string, string> m_authToLocalSceneMapping = new Dictionary<string, string>(); //1-1 mapping from authoratative scene to local shadow scene
|
||||
private Dictionary<string, Scene> m_localScenesByName = new Dictionary<string,Scene>(); //name and references to local scenes
|
||||
private Dictionary<string, AuthSceneInfo> m_authScenesInfoByName = new Dictionary<string,AuthSceneInfo>(); //info of each auth. scene's connector port, stored by each scene's name
|
||||
private Dictionary<string, PhysEngineToSceneConnector> m_PEToSceneConnectors = new Dictionary<string, PhysEngineToSceneConnector>(); //connector for each auth. scene
|
||||
private Dictionary<string, AuthSceneInfo> m_authScenesInfoByLoc = new Dictionary<string,AuthSceneInfo>(); //IP and port number of each auth. scene's connector port
|
||||
private string LogHeader = "[PhysEngineToSceneConnectorModule]";
|
||||
private PhysEngineToSceneConnector m_idlePEToSceneConnector = null;
|
||||
|
||||
//quark information
|
||||
//private int QuarkInfo.SizeX;
|
||||
//private int QuarkInfo.SizeY;
|
||||
//private string m_quarkListString;
|
||||
private string m_subscriptionSpaceString;
|
||||
|
||||
public IConfig SyncConfig
|
||||
{
|
||||
get { return m_syncConfig; }
|
||||
}
|
||||
|
||||
public bool DebugWithViewer
|
||||
{
|
||||
get { return m_debugWithViewer; }
|
||||
}
|
||||
|
||||
//Record the locX and locY of one auth. scene (identified by addr:port) this PhysEngine connects to
|
||||
public void RecordSceneLocation(string addr, int port, uint locX, uint locY)
|
||||
{
|
||||
string loc = SceneLocToString(locX, locY);
|
||||
if (m_authScenesInfoByLoc.ContainsKey(loc))
|
||||
{
|
||||
m_log.Warn(": have already registered info for Scene at " + loc);
|
||||
m_authScenesInfoByLoc.Remove(loc);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, AuthSceneInfo> valPair in m_authScenesInfoByName)
|
||||
{
|
||||
AuthSceneInfo authSceneInfo = valPair.Value;
|
||||
if (authSceneInfo.Addr == addr && authSceneInfo.Port == port)
|
||||
{
|
||||
authSceneInfo.LocX = (int)locX;
|
||||
authSceneInfo.LocY = (int)locY;
|
||||
m_authScenesInfoByLoc.Add(loc, authSceneInfo);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Set the property of a prim located in the given scene (identified by locX, locY)
|
||||
/// </summary>
|
||||
/// <param name="locX"></param>
|
||||
/// <param name="locY"></param>
|
||||
/// <param name="primID"></param>
|
||||
/// <param name="pName"></param>
|
||||
/// <param name="pValue"></param>
|
||||
public void SendSetPrimProperties(uint locX, uint locY, UUID primID, string pName, object pValue)
|
||||
{
|
||||
if (!Active || !Synced)
|
||||
return;
|
||||
|
||||
PhysEngineToSceneConnector connector = GetPEToSceneConnector(locX, locY);
|
||||
connector.SendSetPrimProperties(primID, pName, pValue);
|
||||
}
|
||||
|
||||
public Scene GetLocalScene(string authSceneName)
|
||||
{
|
||||
if (!m_authToLocalSceneMapping.ContainsKey(authSceneName))
|
||||
{
|
||||
m_log.Warn(LogHeader + ": no authoritative scene with name "+authSceneName+" recorded");
|
||||
return null;
|
||||
}
|
||||
string localSceneName = m_authToLocalSceneMapping[authSceneName];
|
||||
if (m_localScenesByName.ContainsKey(localSceneName))
|
||||
{
|
||||
return m_localScenesByName[localSceneName];
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private string SceneLocToString(uint locX, uint locY)
|
||||
{
|
||||
string loc = locX + "-" + locY;
|
||||
return loc;
|
||||
}
|
||||
|
||||
//Get the right instance of PhysEngineToSceneConnector, given the location of the authoritative scene
|
||||
private PhysEngineToSceneConnector GetPEToSceneConnector(uint locX, uint locY)
|
||||
{
|
||||
string loc = SceneLocToString(locX, locY);
|
||||
if (!m_authScenesInfoByLoc.ContainsKey(loc))
|
||||
return null;
|
||||
string authSceneName = m_authScenesInfoByLoc[loc].Name;
|
||||
if (!m_PEToSceneConnectors.ContainsKey(authSceneName))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return m_PEToSceneConnectors[authSceneName];
|
||||
}
|
||||
|
||||
|
||||
private void RecordLocalAuthSceneMappings(string localSceneName, string authSceneName)
|
||||
{
|
||||
if (m_localToAuthSceneMapping.ContainsKey(localSceneName))
|
||||
{
|
||||
m_log.Warn(LogHeader + ": already registered " + localSceneName+", authScene was recorded as "+ m_localToAuthSceneMapping[localSceneName]);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_localToAuthSceneMapping.Add(localSceneName, authSceneName);
|
||||
}
|
||||
if (m_authToLocalSceneMapping.ContainsKey(authSceneName))
|
||||
{
|
||||
m_log.Warn(LogHeader + ": already registered " + authSceneName + ", authScene was recorded as " + m_authToLocalSceneMapping[authSceneName]);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_authToLocalSceneMapping.Add(authSceneName, localSceneName);
|
||||
}
|
||||
}
|
||||
|
||||
//Get the name of the authoritative scene the given local scene maps to. Return null if not found.
|
||||
private string GetAuthSceneName(string localSceneName)
|
||||
{
|
||||
if (m_localToAuthSceneMapping.ContainsKey(localSceneName))
|
||||
{
|
||||
m_log.Warn(LogHeader + ": " + localSceneName + " not registered in m_localToAuthSceneMapping");
|
||||
return null;
|
||||
}
|
||||
return m_localToAuthSceneMapping[localSceneName];
|
||||
}
|
||||
|
||||
//get the name of the local scene the given authoritative scene maps to. Return null if not found.
|
||||
private string GetLocalSceneName(string authSceneName)
|
||||
{
|
||||
if (!m_authToLocalSceneMapping.ContainsKey(authSceneName))
|
||||
{
|
||||
m_log.Warn(LogHeader + ": " + authSceneName + " not registered in m_authToLocalSceneMapping");
|
||||
return null;
|
||||
}
|
||||
return m_authToLocalSceneMapping[authSceneName];
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
public void OnPopulateLocalSceneList(List<Scene> localScenes)
|
||||
//public void OnPopulateLocalSceneList(List<Scene> localScenes, string[] cmdparams)
|
||||
{
|
||||
if (!Active)
|
||||
return;
|
||||
|
||||
//populate the dictionary m_localScenes
|
||||
foreach (Scene lScene in localScenes)
|
||||
{
|
||||
string name = lScene.RegionInfo.RegionName;
|
||||
if(!m_localScenesByName.ContainsKey(name)){
|
||||
m_log.Warn(LogHeader+": has not reigstered a local scene named "+name);
|
||||
continue;
|
||||
}
|
||||
m_localScenesByName[name] = lScene;
|
||||
|
||||
//lScene.RegionSyncMode = m_regionSyncMode;
|
||||
lScene.IsOutsideScenes = IsOutSideSceneSubscriptions;
|
||||
}
|
||||
|
||||
//test position conversion
|
||||
/*
|
||||
//Vector3 pos = new Vector3(290, 100, 10);
|
||||
uint preLocX = Convert.ToUInt32(cmdparams[2]);
|
||||
uint preLocY = Convert.ToUInt32(cmdparams[3]);
|
||||
float posX = (float)Convert.ToDouble(cmdparams[4]);
|
||||
float posY = (float)Convert.ToDouble(cmdparams[5]);
|
||||
float posZ = (float)Convert.ToDouble(cmdparams[6]);
|
||||
Vector3 pos = new Vector3(posX, posY, posZ);
|
||||
uint locX, locY;
|
||||
Vector3 newPos;
|
||||
ConvertPosition(1000, 1000, pos, out locX, out locY, out newPos);
|
||||
* */
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private string GetAllSceneNames()
|
||||
{
|
||||
string scenes = "";
|
||||
foreach (KeyValuePair<string, Scene> valPair in m_localScenesByName)
|
||||
{
|
||||
Scene lScene = valPair.Value;
|
||||
string authScene = m_localToAuthSceneMapping[lScene.RegionInfo.RegionName];
|
||||
scenes += authScene + ",";
|
||||
|
||||
}
|
||||
return scenes;
|
||||
}
|
||||
|
||||
//public bool IsOutSideSceneSubscriptions(Scene currentScene, Vector3 pos)
|
||||
public bool IsOutSideSceneSubscriptions(uint locX, uint locY, Vector3 pos)
|
||||
{
|
||||
string sceneNames = GetAllSceneNames();
|
||||
m_log.Debug(LogHeader + ": IsOutSideSceneSubscriptions called. Conceptually, we are checking inside scene-subscriptions: " + sceneNames);
|
||||
|
||||
//First, convert the position to a scene s.t. the attempting position is contained withing that scene
|
||||
uint curLocX, curLocY;
|
||||
Vector3 curPos;
|
||||
bool converted = ConvertPosition(locX, locY, pos, out curLocX, out curLocY, out curPos);
|
||||
|
||||
if (!converted)
|
||||
{
|
||||
m_log.Warn("("+locX+","+locY+","+pos+")"+" converts to scenes with negative coordinates.");
|
||||
return false;
|
||||
}
|
||||
//See of the quark identified by (curLocX,curLocY) is one we subscribed to
|
||||
string sceneLoc = SceneLocToString(curLocX, curLocY);
|
||||
if (m_authScenesInfoByLoc.ContainsKey(sceneLoc))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//When the offset position is outside the range of current scene, convert it to the offset position in the right quark.
|
||||
//Return null if the new scene's left-bottom corner X or Y value is negative.
|
||||
//Assumption: A position is uniquely identified by (locX, locY, offsetPos).
|
||||
private bool ConvertPosition(uint preLocX, uint preLocY, Vector3 prePos, out uint curLocX, out uint curLocY, out Vector3 curPos)
|
||||
{
|
||||
Vector3 newPos;
|
||||
int newLocX;
|
||||
int newLocY;
|
||||
//code copied from EntityTransferModule.Cross()
|
||||
|
||||
newPos = prePos;
|
||||
newLocX = (int)preLocX;
|
||||
newLocY = (int)preLocY;
|
||||
|
||||
int changeX = 1;
|
||||
int changeY = 1;
|
||||
|
||||
//Adjust the X values, if going east, changeX is positive, otherwise, it is negative
|
||||
if (prePos.X >= 0)
|
||||
{
|
||||
changeX = (int)(prePos.X / (int)Constants.RegionSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
changeX = (int)(prePos.X / (int)Constants.RegionSize) - 1 ;
|
||||
}
|
||||
newLocX = (int)preLocX + changeX;
|
||||
newPos.X = prePos.X - (changeX * Constants.RegionSize);
|
||||
|
||||
if (prePos.Y >= 0)
|
||||
{
|
||||
changeY = (int)(prePos.Y / (int)Constants.RegionSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
changeY = (int)(prePos.Y / (int)Constants.RegionSize) - 1;
|
||||
}
|
||||
changeY = (int)(prePos.Y / (int)Constants.RegionSize);
|
||||
newLocY = (int)preLocY + changeY;
|
||||
newPos.Y = prePos.Y - (changeY * Constants.RegionSize);
|
||||
|
||||
curLocX = (uint)newLocX;
|
||||
curLocY = (uint)newLocY;
|
||||
curPos = newPos;
|
||||
|
||||
if (newLocX < 0 || newLocY < 0)
|
||||
{
|
||||
//reset the position
|
||||
curLocX = preLocX;
|
||||
curLocY = preLocY;
|
||||
if (newLocX < 0)
|
||||
{
|
||||
curPos.X = 2;
|
||||
}
|
||||
if(newLocY<0)
|
||||
{
|
||||
curPos.Y = 2;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
private void DebugSceneStats()
|
||||
{
|
||||
return;
|
||||
/*
|
||||
List<ScenePresence> avatars = m_scene.GetAvatars();
|
||||
List<EntityBase> entities = m_scene.GetEntities();
|
||||
m_log.WarnFormat("There are {0} avatars and {1} entities in the scene", avatars.Count, entities.Count);
|
||||
*/
|
||||
}
|
||||
|
||||
#region Console Command Interface
|
||||
//IMPORTANT: these functions should only be actived for the PhysEngineToSceneConnectorModule that is associated with the valid local scene
|
||||
|
||||
private void InstallInterfaces()
|
||||
{
|
||||
Command cmdSyncStart = new Command("start", CommandIntentions.COMMAND_HAZARDOUS, SyncStart, "Begins synchronization with RegionSyncServer.");
|
||||
//cmdSyncStart.AddArgument("server_port", "The port of the server to synchronize with", "Integer");
|
||||
|
||||
Command cmdSyncStop = new Command("stop", CommandIntentions.COMMAND_HAZARDOUS, SyncStop, "Stops synchronization with RegionSyncServer.");
|
||||
//cmdSyncStop.AddArgument("server_address", "The IP address of the server to synchronize with", "String");
|
||||
//cmdSyncStop.AddArgument("server_port", "The port of the server to synchronize with", "Integer");
|
||||
|
||||
Command cmdSyncStatus = new Command("status", CommandIntentions.COMMAND_HAZARDOUS, SyncStatus, "Displays synchronization status.");
|
||||
|
||||
//The following two commands are more for easier debugging purpose
|
||||
Command cmdSyncSetQuarks = new Command("quarkSpace", CommandIntentions.COMMAND_HAZARDOUS, SetQuarkList, "Set the set of quarks to subscribe to. For debugging purpose. Should be issued before \"sync start\"");
|
||||
cmdSyncSetQuarks.AddArgument("quarkSpace", "The (rectangle) space of quarks to subscribe, represented by x0_y0,x1_y1, the left-bottom and top-right corners of the rectangel space", "String");
|
||||
|
||||
Command cmdSyncSetQuarkSize = new Command("quarksize", CommandIntentions.COMMAND_HAZARDOUS, SetQuarkSize, "Set the size of each quark. For debugging purpose. Should be issued before \"sync quarks\"");
|
||||
cmdSyncSetQuarkSize.AddArgument("quarksizeX", "The size on x axis of each quark", "Integer");
|
||||
cmdSyncSetQuarkSize.AddArgument("quarksizeY", "The size on y axis of each quark", "Integer");
|
||||
|
||||
Command cmdSyncRegister = new Command("register", CommandIntentions.COMMAND_HAZARDOUS, SyncRegister, "Register as an idle script engine. Sync'ing with Scene won't start until \"sync start\". ");
|
||||
|
||||
//For debugging load balancing and migration process
|
||||
Command cmdSyncStartLB = new Command("startLB", CommandIntentions.COMMAND_HAZARDOUS, SyncStartLB, "Register as an idle script engine. Sync'ing with Scene won't start until \"sync start\". ");
|
||||
|
||||
m_commander.RegisterCommand("start", cmdSyncStart);
|
||||
m_commander.RegisterCommand("stop", cmdSyncStop);
|
||||
m_commander.RegisterCommand("status", cmdSyncStatus);
|
||||
m_commander.RegisterCommand("quarkSpace", cmdSyncSetQuarks);
|
||||
m_commander.RegisterCommand("register", cmdSyncRegister);
|
||||
m_commander.RegisterCommand("startLB", cmdSyncStartLB);
|
||||
|
||||
lock (m_scene)
|
||||
{
|
||||
// Add this to our scene so scripts can call these functions
|
||||
m_scene.RegisterModuleCommander(m_commander);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Processes commandline input. Do not call directly.
|
||||
/// </summary>
|
||||
/// <param name="args">Commandline arguments</param>
|
||||
private void EventManager_OnPluginConsole(string[] args)
|
||||
{
|
||||
if (args[0] == "sync")
|
||||
{
|
||||
if (args.Length == 1)
|
||||
{
|
||||
m_commander.ProcessConsoleCommand("help", new string[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
string[] tmpArgs = new string[args.Length - 2];
|
||||
int i;
|
||||
for (i = 2; i < args.Length; i++)
|
||||
tmpArgs[i - 2] = args[i];
|
||||
|
||||
m_commander.ProcessConsoleCommand(args[1], tmpArgs);
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncStart(Object[] args)
|
||||
{
|
||||
lock (m_client_lock)
|
||||
{
|
||||
//if (m_scriptEngineToSceneConnector != null)
|
||||
if(m_PEToSceneConnectors.Count>0)
|
||||
{
|
||||
string authScenes = "";
|
||||
foreach (KeyValuePair<string, PhysEngineToSceneConnector> valPair in m_PEToSceneConnectors)
|
||||
{
|
||||
authScenes += valPair.Key + ", ";
|
||||
}
|
||||
m_log.WarnFormat(LogHeader+": Already synchronized to "+authScenes);
|
||||
return;
|
||||
}
|
||||
//m_log.Warn("[REGION SYNC CLIENT MODULE] Starting synchronization");
|
||||
m_log.Warn(LogHeader + ": Starting RegionSyncPhysEngine");
|
||||
|
||||
if (m_sceneNum > 1)
|
||||
{
|
||||
//If there is no arguments following "sync start", then be default we will connect to one or more scenes.
|
||||
//we need to create a connector to each authoritative scene
|
||||
foreach (KeyValuePair<string, AuthSceneInfo> valPair in m_authScenesInfoByName)
|
||||
{
|
||||
string authSceneName = valPair.Key;
|
||||
AuthSceneInfo authSceneInfo = valPair.Value;
|
||||
|
||||
//create a new connector, the local end of each connector, however, is linked to the ValidScene only,
|
||||
//since all objects will be contained in this scene only
|
||||
PhysEngineToSceneConnector scriptEngineToSceneConnector = new PhysEngineToSceneConnector(m_scene, authSceneInfo.Addr, authSceneInfo.Port, m_debugWithViewer, authSceneName, m_syncConfig);
|
||||
if (scriptEngineToSceneConnector.Start())
|
||||
{
|
||||
m_PEToSceneConnectors.Add(authSceneName, scriptEngineToSceneConnector);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//Only one remote scene to connect to. Subscribe to whatever specified in the config file.
|
||||
//List<string> quarkStringList = RegionSyncUtil.QuarkStringToStringList(m_quarkListString);
|
||||
//InitPhysEngineToSceneConnector(quarkStringList);
|
||||
InitPhysEngineToSceneConnector(m_subscriptionSpaceString);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncRegister(Object[] args)
|
||||
{
|
||||
//This should not happen. No-validLocalScene should not have register handlers for the command
|
||||
//if (m_scene.RegionInfo.RegionName != m_validLocalScene)
|
||||
// return;
|
||||
|
||||
//Registration only, no state sync'ing yet. So only start the connector for the validLocalScene. (For now, we only test this with one scene, and
|
||||
//quarks are smaller than a 256x256 scene.
|
||||
string authSceneName = m_localToAuthSceneMapping[m_validLocalScene];
|
||||
AuthSceneInfo authSceneInfo = m_authScenesInfoByName[authSceneName];
|
||||
m_idlePEToSceneConnector = new PhysEngineToSceneConnector(m_scene, authSceneInfo.Addr, authSceneInfo.Port, m_debugWithViewer, authSceneName, m_syncConfig);
|
||||
m_idlePEToSceneConnector.RegisterIdle();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The given PhysEngineToSceneConnector, after having connected to the Scene (called its Start()), will
|
||||
/// call this function to remove it self as an idle connector, and to be recorded as one working connector.
|
||||
/// </summary>
|
||||
/// <param name="seToSceneConnector"></param>
|
||||
public void RecordSyncStartAfterLoadMigration(PhysEngineToSceneConnector seToSceneConnector)
|
||||
{
|
||||
foreach (KeyValuePair<string, AuthSceneInfo> valPair in m_authScenesInfoByName)
|
||||
{
|
||||
string authSceneName = valPair.Key;
|
||||
AuthSceneInfo authSceneInfo = valPair.Value;
|
||||
|
||||
string localScene = m_authToLocalSceneMapping[authSceneName];
|
||||
|
||||
if (localScene != m_scene.RegionInfo.RegionName)
|
||||
continue;
|
||||
|
||||
if (m_PEToSceneConnectors.ContainsKey(authSceneName))
|
||||
{
|
||||
m_log.Warn(LogHeader + ": Connector to " + authSceneName + " is already considered connected");
|
||||
return;
|
||||
}
|
||||
|
||||
m_PEToSceneConnectors.Add(authSceneName, seToSceneConnector);
|
||||
//there should only be one element in the dictionary if we reach this loop, anyway, we break from it.
|
||||
break;
|
||||
}
|
||||
m_idlePEToSceneConnector = null;
|
||||
}
|
||||
|
||||
private void SyncStartLB(Object[] args)
|
||||
{
|
||||
string authSceneName = m_localToAuthSceneMapping[m_validLocalScene];
|
||||
PhysEngineToSceneConnector sceneConnector = m_PEToSceneConnectors[authSceneName];
|
||||
// TODO: load balancing. Next line commented out
|
||||
// sceneConnector.SendLoadBalanceRequest();
|
||||
}
|
||||
|
||||
private void SetQuarkList(Object[] args)
|
||||
{
|
||||
m_subscriptionSpaceString = (string)args[0];
|
||||
|
||||
InitPhysEngineToSceneConnector(m_subscriptionSpaceString);
|
||||
}
|
||||
|
||||
private void SetQuarkSize(Object[] args)
|
||||
{
|
||||
QuarkInfo.SizeX = (int)args[0];
|
||||
QuarkInfo.SizeY = (int)args[1];
|
||||
|
||||
}
|
||||
|
||||
private void InitPhysEngineToSceneConnector(string space)
|
||||
{
|
||||
|
||||
foreach (KeyValuePair<string, AuthSceneInfo> valPair in m_authScenesInfoByName)
|
||||
{
|
||||
string authSceneName = valPair.Key;
|
||||
AuthSceneInfo authSceneInfo = valPair.Value;
|
||||
|
||||
string localScene = m_authToLocalSceneMapping[authSceneName];
|
||||
|
||||
if (localScene != m_scene.RegionInfo.RegionName)
|
||||
continue;
|
||||
|
||||
//create a new connector, the local end of each connector, however, is set of the ValidScene only,
|
||||
//since all objects will be contained in this scene only
|
||||
PhysEngineToSceneConnector scriptEngineToSceneConnector = new PhysEngineToSceneConnector(m_scene, authSceneInfo.Addr, authSceneInfo.Port,
|
||||
m_debugWithViewer, authSceneName, space, m_syncConfig);
|
||||
if (scriptEngineToSceneConnector.Start())
|
||||
{
|
||||
m_PEToSceneConnectors.Add(authSceneName, scriptEngineToSceneConnector);
|
||||
}
|
||||
|
||||
break; //there should only be one element in the dictionary if we reach this loop, anyway, we break from it.
|
||||
}
|
||||
}
|
||||
|
||||
private void SyncStop(Object[] args)
|
||||
{
|
||||
lock (m_client_lock)
|
||||
{
|
||||
//if (m_scriptEngineToSceneConnector == null)
|
||||
if(m_PEToSceneConnectors.Count==0 && m_idlePEToSceneConnector==null)
|
||||
{
|
||||
m_log.WarnFormat("[REGION SYNC PHYSICS ENGINE MODULE] Already stopped");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_PEToSceneConnectors.Count > 0)
|
||||
{
|
||||
foreach (KeyValuePair<string, PhysEngineToSceneConnector> valPair in m_PEToSceneConnectors)
|
||||
{
|
||||
PhysEngineToSceneConnector connector = valPair.Value;
|
||||
if (connector == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
connector.Stop();
|
||||
}
|
||||
m_PEToSceneConnectors.Clear();
|
||||
}
|
||||
else if (m_idlePEToSceneConnector != null)
|
||||
{
|
||||
m_idlePEToSceneConnector.Stop();
|
||||
m_idlePEToSceneConnector = null;
|
||||
}
|
||||
|
||||
//m_scriptEngineToSceneConnector.Stop();
|
||||
//m_scriptEngineToSceneConnector = null;
|
||||
m_log.Warn(LogHeader+": Stopping synchronization");
|
||||
}
|
||||
|
||||
m_authScenesInfoByLoc.Clear();
|
||||
|
||||
//save script state and stop script instances
|
||||
// TODO: Load balancing. next line commented out to compile
|
||||
// m_scene.EventManager.TriggerPhysEngineSyncStop();
|
||||
//remove all objects
|
||||
m_scene.DeleteAllSceneObjects();
|
||||
|
||||
}
|
||||
|
||||
private void SyncStatus(Object[] args)
|
||||
{
|
||||
lock (m_client_lock)
|
||||
{
|
||||
if (m_PEToSceneConnectors.Count == 0)
|
||||
{
|
||||
m_log.WarnFormat("[REGION SYNC PHYSICS ENGINE MODULE] Not currently synchronized");
|
||||
return;
|
||||
}
|
||||
m_log.WarnFormat("[REGION SYNC PHYSICS ENGINE MODULE] Synchronized");
|
||||
foreach (KeyValuePair<string, PhysEngineToSceneConnector> pair in m_PEToSceneConnectors)
|
||||
{
|
||||
PhysEngineToSceneConnector sceneConnector = pair.Value;
|
||||
sceneConnector.ReportStatus();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -10,7 +10,8 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
|
|||
{
|
||||
Null,
|
||||
ClientManager,
|
||||
ScriptEngine
|
||||
ScriptEngine,
|
||||
PhysicsEngine
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
|
@ -0,0 +1,393 @@
|
|||
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 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
|
||||
{
|
||||
#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 lastTotalCount;
|
||||
private int lastLocalCount;
|
||||
private int lastRemoteCount;
|
||||
|
||||
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 int m_connection_number;
|
||||
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 GetStats()
|
||||
{
|
||||
int syncedAvCount;
|
||||
string ret;
|
||||
//lock (m_syncRoot)
|
||||
// syncedAvCount = m_syncedAvatars.Count;
|
||||
lock (stats)
|
||||
{
|
||||
double secondsSinceLastStats = DateTime.Now.Subtract(lastStatTime).TotalSeconds;
|
||||
lastStatTime = DateTime.Now;
|
||||
|
||||
ret = String.Format("[{0,4}/{1,4}], [{2,4}/{3,4}], [{4,4}/{5,4}], [{6,4} ({7,4})], [{8,8} ({9,8:00.00})], [{10,4} ({11,4})], [{12,8} ({13,8:00.00})], [{14,8} ({15,4}]",
|
||||
//lastTotalCount, totalAvCount, // TOTAL AVATARS
|
||||
//lastLocalCount, syncedAvCount, // LOCAL TO THIS CLIENT VIEW
|
||||
//lastRemoteCount, totalAvCount - syncedAvCount, // REMOTE (SHOULD = TOTAL - LOCAL)
|
||||
msgsIn, (int)(msgsIn / secondsSinceLastStats),
|
||||
bytesIn, 8 * (bytesIn / secondsSinceLastStats / 1000000), // IN
|
||||
msgsOut, (int)(msgsOut / secondsSinceLastStats),
|
||||
bytesOut, 8 * (bytesOut / secondsSinceLastStats / 1000000), // OUT
|
||||
pollBlocks, (int)(pollBlocks / secondsSinceLastStats)); // NUMBER OF TIMES WE BLOCKED WRITING TO SOCKET
|
||||
|
||||
msgsIn = msgsOut = bytesIn = bytesOut = pollBlocks = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Check if the client is connected
|
||||
public bool Connected
|
||||
{ get { return m_tcpclient.Connected; } }
|
||||
|
||||
//private int QuarkInfo.SizeX;
|
||||
//private int QuarkInfo.SizeY;
|
||||
//private List<QuarkInfo> m_quarkSubscriptions;
|
||||
Dictionary<string, QuarkInfo> m_quarkSubscriptions;
|
||||
public Dictionary<string, QuarkInfo> QuarkSubscriptionList
|
||||
{
|
||||
get { return m_quarkSubscriptions; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
#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;
|
||||
|
||||
//QuarkInfo.SizeX = quarkSizeX;
|
||||
//QuarkInfo.SizeY = quarkSizeY;
|
||||
|
||||
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();
|
||||
|
||||
//tell the remote script engine about the locX, locY of this authoritative scene
|
||||
SendSceneLoc();
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
m_log.WarnFormat("{0} Unable to handle unsupported message type", LogHeader);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//For simplicity, we assume the subscription sent by PhysEngine is legistimate (no overlapping with other script engines, etc)
|
||||
private void HandleQuarkSubscription(RegionSyncMessage msg)
|
||||
{
|
||||
string quarkString = Encoding.ASCII.GetString(msg.Data, 0, msg.Length);
|
||||
m_log.Debug(LogHeader + ": received quark-string: " + quarkString);
|
||||
|
||||
List<string> quarkStringList = RegionSyncUtil.QuarkStringToStringList(quarkString);
|
||||
//m_quarkSubscriptions = RegionSyncUtil.GetQuarkInfoList(quarkStringList, QuarkInfo.SizeX, QuarkInfo.SizeY);
|
||||
List<QuarkInfo> quarkList = RegionSyncUtil.GetQuarkInfoList(quarkStringList);
|
||||
m_syncServer.RegisterQuarkSubscription(quarkList, this);
|
||||
|
||||
m_quarkSubscriptions = new Dictionary<string, QuarkInfo>();
|
||||
foreach (QuarkInfo quark in quarkList)
|
||||
{
|
||||
m_quarkSubscriptions.Add(quark.QuarkStringRepresentation, quark);
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{ }
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
m_log.WarnFormat("{0} Physics Engine has disconnected.", LogHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SendObjectUpdate(RegionSyncMessage.MsgType msgType, SceneObjectGroup sog)
|
||||
{
|
||||
Send(PrepareObjectUpdateMessage(msgType, sog));
|
||||
}
|
||||
|
||||
#endregion Send/Receive messages to/from the remote Physics Engine
|
||||
|
||||
#region Spacial query functions (should be eventually implemented within Scene)
|
||||
|
||||
//This should be a function of Scene, but since we don't have the quark concept in Scene yet,
|
||||
//for now we implement it here.
|
||||
//Ideally, for quark based space representation, the Scene has a list of quarks, and each quark points
|
||||
//to a list of objects within that quark. Then it's much easier to return the right set of objects within
|
||||
//a certain space. (Or use DB that supports spatial queries.)
|
||||
List<SceneObjectGroup> GetObjectsInGivenSpace(Scene scene, Dictionary<string, QuarkInfo> quarkSubscriptions)
|
||||
{
|
||||
List<EntityBase> entities = m_scene.GetEntities();
|
||||
List<SceneObjectGroup> sogList = new List<SceneObjectGroup>();
|
||||
foreach (EntityBase e in entities)
|
||||
{
|
||||
if (e is SceneObjectGroup)
|
||||
{
|
||||
SceneObjectGroup sog = (SceneObjectGroup)e;
|
||||
string quarkID = RegionSyncUtil.GetQuarkIDByPosition(sog.AbsolutePosition);
|
||||
if (m_quarkSubscriptions.ContainsKey(quarkID))
|
||||
{
|
||||
sogList.Add(sog);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sogList;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Load balancing functions
|
||||
public void SendLoadBalanceRejection(string response)
|
||||
{
|
||||
RegionSyncMessage msg = new RegionSyncMessage(RegionSyncMessage.MsgType.LoadBalanceRejection, response);
|
||||
Send(msg);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -0,0 +1,480 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using log4net;
|
||||
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.StructuredData;
|
||||
using OpenSim.Region.Framework.Scenes.Serialization;
|
||||
|
||||
namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
|
||||
{
|
||||
//Information of a registered idle physics engine.
|
||||
//Note, this is a temporary solution to inlcude idle physics engines here.
|
||||
//In the future, there might be a independent load balaner that keeps track
|
||||
//of available idle hardware.
|
||||
public class IdlePhysEngineInfo
|
||||
{
|
||||
public TcpClient TClient;
|
||||
//public IPAddress PhysEngineIPAddr;
|
||||
//public int PhysEnginePort;
|
||||
public string ID;
|
||||
|
||||
//Will be used to store the overloaded PE that has send LB request and paired with this idle PE
|
||||
public SceneToPhysEngineConnector AwaitOverloadedSE=null;
|
||||
|
||||
public IdlePhysEngineInfo(TcpClient tclient)
|
||||
{
|
||||
if(tclient==null) return;
|
||||
TClient = tclient;
|
||||
IPAddress ipAddr = ((IPEndPoint)tclient.Client.RemoteEndPoint).Address;
|
||||
int port = ((IPEndPoint)tclient.Client.RemoteEndPoint).Port;
|
||||
ID = ipAddr.ToString()+":"+port;
|
||||
}
|
||||
}
|
||||
|
||||
//Here is the per actor type listening server for physics Engines.
|
||||
public class SceneToPhysEngineSyncServer
|
||||
{
|
||||
#region SceneToPhysEngineSyncServer members
|
||||
// Set the addr and port for TcpListener
|
||||
private IPAddress m_addr;
|
||||
private Int32 m_port;
|
||||
|
||||
//this field is only meaning for the QuarkInfo records on the Scene side
|
||||
private SceneToPhysEngineConnector m_peConnector=null;
|
||||
public SceneToPhysEngineConnector PEConnector
|
||||
{
|
||||
get { return m_peConnector; }
|
||||
set { m_peConnector = value; }
|
||||
}
|
||||
|
||||
private int peCounter;
|
||||
|
||||
// The local scene.
|
||||
private Scene m_scene;
|
||||
|
||||
private ILog m_log;
|
||||
|
||||
// The listener and the thread which listens for connections from client managers
|
||||
private TcpListener m_listener;
|
||||
private Thread m_listenerThread;
|
||||
|
||||
private object m_physEngineConnector_lock = new object();
|
||||
//private Dictionary<string, SceneToPhysEngineConnector> m_physEngineConnectors = new Dictionary<string, SceneToPhysEngineConnector>();
|
||||
private List<SceneToPhysEngineConnector> m_physEngineConnectors = new List<SceneToPhysEngineConnector>();
|
||||
|
||||
//list of idle physics engines that have registered.
|
||||
private List<IdlePhysEngineInfo> m_idlePhysEngineList = new List<IdlePhysEngineInfo>();
|
||||
|
||||
//List of all quarks, each using the concatenation of x,y values of its left-bottom corners,
|
||||
// where the x,y values are the offset position in the scene.
|
||||
private Dictionary<string, QuarkInfo> m_quarksInScene = new Dictionary<string, QuarkInfo>();
|
||||
|
||||
private string LogHeader = "[SCENE TO PHYS ENGINE SYNC SERVER]";
|
||||
|
||||
//Quark related info
|
||||
//private int QuarkInfo.SizeX;
|
||||
//private int QuarkInfo.SizeY;
|
||||
|
||||
// Check if any of the client views are in a connected state
|
||||
public bool Synced
|
||||
{
|
||||
get
|
||||
{
|
||||
return (m_physEngineConnectors.Count > 0);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
// Constructor
|
||||
public SceneToPhysEngineSyncServer(Scene scene, string addr, int port)
|
||||
{
|
||||
if (QuarkInfo.SizeX == -1 || QuarkInfo.SizeY == -1)
|
||||
{
|
||||
m_log.Error(LogHeader + " QuarkInfo.SizeX or QuarkInfo.SizeY has not been configured yet.");
|
||||
Environment.Exit(0); ;
|
||||
}
|
||||
|
||||
m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
//m_log.Warn(LogHeader + "Constructed");
|
||||
m_scene = scene;
|
||||
m_addr = IPAddress.Parse(addr);
|
||||
m_port = port;
|
||||
|
||||
InitQuarksInScene();
|
||||
SubscribeToEvents();
|
||||
}
|
||||
|
||||
|
||||
private void SubscribeToEvents()
|
||||
{
|
||||
}
|
||||
|
||||
private void UnSubscribeToEvents()
|
||||
{
|
||||
}
|
||||
|
||||
// Start the server
|
||||
public void Start()
|
||||
{
|
||||
m_listenerThread = new Thread(new ThreadStart(Listen));
|
||||
m_listenerThread.Name = "SceneToPhysEngineSyncServer Listener";
|
||||
m_log.WarnFormat(LogHeader + ": Starting {0} thread", m_listenerThread.Name);
|
||||
m_listenerThread.Start();
|
||||
//m_log.Warn("[REGION SYNC SERVER] Started");
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Stop the server and disconnect all RegionSyncClients
|
||||
public void Shutdown()
|
||||
{
|
||||
// Stop the listener and listening thread so no new clients are accepted
|
||||
m_listener.Stop();
|
||||
m_listenerThread.Abort();
|
||||
m_listenerThread = null;
|
||||
|
||||
// Stop all existing SceneTOSEConnectors
|
||||
//TO FINISH
|
||||
foreach (SceneToPhysEngineConnector peConnector in m_physEngineConnectors)
|
||||
{
|
||||
peConnector.Shutdown();
|
||||
}
|
||||
m_physEngineConnectors.Clear();
|
||||
|
||||
UnSubscribeToEvents();
|
||||
}
|
||||
|
||||
private void InitQuarksInScene()
|
||||
{
|
||||
List<QuarkInfo> quarkList = RegionSyncUtil.GetAllQuarksInScene();
|
||||
foreach (QuarkInfo quark in quarkList)
|
||||
{
|
||||
m_quarksInScene.Add(quark.QuarkStringRepresentation, quark);
|
||||
}
|
||||
}
|
||||
|
||||
public void RegisterQuarkSubscription(List<QuarkInfo> quarkSubscriptions, SceneToPhysEngineConnector peConnector)
|
||||
{
|
||||
foreach (QuarkInfo quark in quarkSubscriptions)
|
||||
{
|
||||
string quarkID = quark.QuarkStringRepresentation;
|
||||
// TODO: does the physics engine connect to quarks. Next line commented out.
|
||||
// m_quarksInScene[quarkID].PEConnector = peConnector;
|
||||
m_log.Debug(LogHeader + ": " + quarkID + " subscribed by "+peConnector.Description);
|
||||
}
|
||||
}
|
||||
|
||||
// Add a connector to a physics engine
|
||||
public void AddSyncedPhysEngine(SceneToPhysEngineConnector peConnector)
|
||||
{
|
||||
lock (m_physEngineConnector_lock)
|
||||
{
|
||||
//Dictionary<string, SceneToPhysEngineConnector> currentlist = m_physEngineConnectors;
|
||||
//Dictionary<string, SceneToPhysEngineConnector> newlist = new Dictionary<string, SceneToPhysEngineConnector>(currentlist);
|
||||
m_physEngineConnectors.Add(peConnector);
|
||||
// Threads holding the previous version of the list can keep using it since
|
||||
// they will not hold it for long and get a new copy next time they need to iterate
|
||||
//m_physEngineConnectors = newlist;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the client view from the list and decrement synced client counter
|
||||
public void RemoveSyncedPhysEngine(SceneToPhysEngineConnector peConnector)
|
||||
{
|
||||
lock (m_physEngineConnector_lock)
|
||||
{
|
||||
//Dictionary<string, SceneToPhysEngineConnector> currentlist = m_physEngineConnectors;
|
||||
//Dictionary<string, SceneToPhysEngineConnector> newlist = new Dictionary<string, SceneToPhysEngineConnector>(currentlist);
|
||||
m_physEngineConnectors.Remove(peConnector);
|
||||
// Threads holding the previous version of the list can keep using it since
|
||||
// they will not hold it for long and get a new copy next time they need to iterate
|
||||
//m_physEngineConnectors = newlist;
|
||||
}
|
||||
}
|
||||
|
||||
// Listen for connections from a new RegionSyncClient
|
||||
// When connected, start the ReceiveLoop for the new client
|
||||
private void Listen()
|
||||
{
|
||||
m_listener = new TcpListener(m_addr, m_port);
|
||||
|
||||
try
|
||||
{
|
||||
// Start listening for clients
|
||||
m_listener.Start();
|
||||
while (true)
|
||||
{
|
||||
// *** Move/Add TRY/CATCH to here, but we don't want to spin loop on the same error
|
||||
m_log.WarnFormat(LogHeader + ": 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;
|
||||
|
||||
ActorStatus actorStatus = GetActorStatus(tcpclient);
|
||||
|
||||
switch (actorStatus)
|
||||
{
|
||||
case ActorStatus.Sync:
|
||||
// Add the SceneToPhysEngineConnector to the list
|
||||
SceneToPhysEngineConnector sceneToPEConnector = new SceneToPhysEngineConnector(++peCounter, m_scene, tcpclient, this);
|
||||
AddSyncedPhysEngine(sceneToPEConnector);
|
||||
break;
|
||||
case ActorStatus.Idle:
|
||||
IdlePhysEngineInfo idleSE = new IdlePhysEngineInfo(tcpclient);
|
||||
m_log.Debug(": adding an idle SE ("+addr+","+port+")");
|
||||
m_idlePhysEngineList.Add(idleSE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
catch (SocketException e)
|
||||
{
|
||||
m_log.WarnFormat(LogHeader + " [Listen] SocketException: {0}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public void RegisterSyncedPhysEngine(SceneToPhysEngineConnector sceneToSEConnector)
|
||||
{
|
||||
//first, remove it from the idle list
|
||||
m_idlePhysEngineList.Remove(sceneToSEConnector);
|
||||
|
||||
//now, added to the synced SE list
|
||||
AddSyncedPhysEngine(sceneToSEConnector);
|
||||
}
|
||||
* */
|
||||
|
||||
|
||||
// Broadcast a message to all connected RegionSyncClients
|
||||
public void SendToAllConnectedPE(RegionSyncMessage msg)
|
||||
{
|
||||
if (m_physEngineConnectors.Count > 0)
|
||||
{
|
||||
m_log.Debug(LogHeader + ": region " + m_scene.RegionInfo.RegionName + " Broadcast to PhysEngine, msg " + msg.Type);
|
||||
foreach (SceneToPhysEngineConnector peConnector in m_physEngineConnectors)
|
||||
{
|
||||
peConnector.Send(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//TO FINISH: Find the right SceneToSEConnector to forward the message
|
||||
public void SendToPE(RegionSyncMessage.MsgType msgType, SceneObjectGroup sog)
|
||||
{
|
||||
SceneToPhysEngineConnector peConnector = GetSceneToPEConnector(sog);
|
||||
if (peConnector != null)
|
||||
{
|
||||
peConnector.SendObjectUpdate(msgType, sog);
|
||||
}
|
||||
}
|
||||
|
||||
//This is to send a message, rsm, to phys engine, and the message is about object SOG. E.g. RemovedObject
|
||||
public void SendToPE(RegionSyncMessage rsm, SceneObjectGroup sog)
|
||||
{
|
||||
SceneToPhysEngineConnector peConnector = GetSceneToPEConnector(sog);
|
||||
if (peConnector != null)
|
||||
{
|
||||
peConnector.Send(rsm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private SceneToPhysEngineConnector GetSceneToPEConnector(SceneObjectGroup sog)
|
||||
{
|
||||
if (m_physEngineConnectors.Count == 0)
|
||||
return null;
|
||||
if (sog == null)
|
||||
{
|
||||
return m_physEngineConnectors[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
//Find the right SceneToSEConnector by the object's position
|
||||
//TO FINISH: Map the object to a quark first, then map the quark to SceneToSEConnector
|
||||
string quarkID = RegionSyncUtil.GetQuarkIDByPosition(sog.AbsolutePosition);
|
||||
// TODO: connection of physics engine to quarks. Next line commented out
|
||||
// SceneToPhysEngineConnector peConnector = m_quarksInScene[quarkID].PEConnector;
|
||||
|
||||
if (PEConnector == null)
|
||||
{
|
||||
m_log.Warn(LogHeader + sog.AbsolutePosition.ToString() + " not covered by any physics engine");
|
||||
}
|
||||
|
||||
return PEConnector;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private ActorStatus GetActorStatus(TcpClient tcpclient)
|
||||
{
|
||||
m_log.Debug(LogHeader+ ": Get Actor status");
|
||||
|
||||
RegionSyncMessage msg = new RegionSyncMessage(tcpclient.GetStream());
|
||||
ActorStatus actorStatus;
|
||||
switch (msg.Type)
|
||||
{
|
||||
case RegionSyncMessage.MsgType.ActorStatus:
|
||||
{
|
||||
string status = Encoding.ASCII.GetString(msg.Data, 0, msg.Length);
|
||||
m_log.Debug(LogHeader + ": recv status: " + status);
|
||||
actorStatus = (ActorStatus)Convert.ToInt32(status);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
m_log.Error(LogHeader + ": Expect Message Type: ActorStatus");
|
||||
RegionSyncMessage.HandleError("[REGION SYNC SERVER]", msg, String.Format("{0} Expect Message Type: ActorType", "[REGION SYNC SERVER]"));
|
||||
return ActorStatus.Null;
|
||||
}
|
||||
}
|
||||
return actorStatus;
|
||||
}
|
||||
|
||||
|
||||
#region Event Handlers
|
||||
|
||||
#endregion Event Handlers
|
||||
|
||||
#region Load balancing members and functions
|
||||
//keep track of idle physics engines that are in the process of load balancing (they are off the idle list, but not a working physics engine yet (not sync'ing with Scene yet)).
|
||||
private Dictionary<string, IdlePhysEngineInfo> m_loadBalancingIdleSEs = new Dictionary<string,IdlePhysEngineInfo>();
|
||||
public void HandleLoadBalanceRequest(SceneToPhysEngineConnector seConnctor)
|
||||
{
|
||||
//Let's start a thread to do the job, so that we can return quickly and don't block on ReceiveLoop()
|
||||
|
||||
Thread partitionThread = new Thread(new ParameterizedThreadStart(TriggerLoadBalanceProcess));
|
||||
partitionThread.Name = "TriggerLoadBalanceProcess";
|
||||
partitionThread.Start((object)seConnctor);
|
||||
}
|
||||
|
||||
public void TriggerLoadBalanceProcess(object arg)
|
||||
{
|
||||
SceneToPhysEngineConnector seConnctor = (SceneToPhysEngineConnector)arg;
|
||||
IdlePhysEngineInfo idlePhysEngineInfo = GetIdlePhysEngineConnector();
|
||||
if (idlePhysEngineInfo != null)
|
||||
{
|
||||
RegionSyncMessage msg = new RegionSyncMessage(RegionSyncMessage.MsgType.LoadMigrationNotice);
|
||||
Send(idlePhysEngineInfo.TClient, msg.ToBytes());
|
||||
m_log.Debug(LogHeader + ": HandleLoadBalanceRequest from " + seConnctor.Description + ", picked idle SE: " + idlePhysEngineInfo.ID);
|
||||
|
||||
//keep track of which overload physics engine is paired up with which idle physics engine
|
||||
idlePhysEngineInfo.AwaitOverloadedSE = seConnctor;
|
||||
m_loadBalancingIdleSEs.Add(idlePhysEngineInfo.ID, idlePhysEngineInfo);
|
||||
|
||||
m_log.Debug("ToSEConnector portal: local -" +
|
||||
((IPEndPoint)idlePhysEngineInfo.TClient.Client.LocalEndPoint).Address.ToString() + ":" + ((IPEndPoint)idlePhysEngineInfo.TClient.Client.LocalEndPoint).Port
|
||||
+ "; remote - " + ((IPEndPoint)idlePhysEngineInfo.TClient.Client.RemoteEndPoint).Address.ToString() + ":"
|
||||
+ ((IPEndPoint)idlePhysEngineInfo.TClient.Client.RemoteEndPoint).Port);
|
||||
|
||||
//Now we expect the idle physics engine to reply back
|
||||
msg = new RegionSyncMessage(idlePhysEngineInfo.TClient.GetStream());
|
||||
if (msg.Type != RegionSyncMessage.MsgType.LoadMigrationListenerInitiated)
|
||||
{
|
||||
m_log.Warn(LogHeader + ": should receive a message of type LoadMigrationListenerInitiated, but received " + msg.Type.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
//Before the load is migrated from overloaded physics engine to the idle engine, sync with the DB to update the state in DB
|
||||
List<EntityBase> entities = m_scene.GetEntities();
|
||||
foreach (EntityBase entity in entities)
|
||||
{
|
||||
if (!entity.IsDeleted && entity is SceneObjectGroup && ((SceneObjectGroup)entity).HasGroupChanged)
|
||||
{
|
||||
m_scene.ForceSceneObjectBackup((SceneObjectGroup)entity);
|
||||
}
|
||||
}
|
||||
|
||||
OSDMap data = DeserializeMessage(msg);
|
||||
if (!data.ContainsKey("ip") || !data.ContainsKey("port") )
|
||||
{
|
||||
m_log.Warn(LogHeader + ": parameters missing in SceneLocation message from Scene, need to have ip, port");
|
||||
return;
|
||||
}
|
||||
//echo the information back to the overloaded physics engine
|
||||
seConnctor.Send(new RegionSyncMessage(RegionSyncMessage.MsgType.LoadBalanceResponse, OSDParser.SerializeJsonString(data)));
|
||||
|
||||
m_log.Debug(LogHeader + " now remove physics engine " + idlePhysEngineInfo.ID + " from idle SE list, and create SceneToPhysEngineConnector to it");
|
||||
//create a SceneToSEConnector for the idle physics engine, who will be sync'ing with this SyncServer soon
|
||||
SceneToPhysEngineConnector sceneToSEConnector = new SceneToPhysEngineConnector(++peCounter, m_scene, idlePhysEngineInfo.TClient, this);
|
||||
//Now remove the physics engine from the idle SE list
|
||||
m_idlePhysEngineList.Remove(idlePhysEngineInfo);
|
||||
//AddSyncedPhysEngine(sceneToSEConnector);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
seConnctor.SendLoadBalanceRejection("no idle physics engines");
|
||||
}
|
||||
}
|
||||
|
||||
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 Send(TcpClient tcpclient, byte[] data)
|
||||
{
|
||||
if (tcpclient.Connected)
|
||||
{
|
||||
try
|
||||
{
|
||||
tcpclient.GetStream().BeginWrite(data, 0, data.Length, ar =>
|
||||
{
|
||||
if (tcpclient.Connected)
|
||||
{
|
||||
try
|
||||
{
|
||||
tcpclient.GetStream().EndWrite(ar);
|
||||
}
|
||||
catch (Exception)
|
||||
{ }
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
m_log.WarnFormat("{0} physics Engine has disconnected.", LogHeader);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IdlePhysEngineInfo GetIdlePhysEngineConnector()
|
||||
{
|
||||
if (m_idlePhysEngineList.Count == 0)
|
||||
return null;
|
||||
IdlePhysEngineInfo idleSEInfo = m_idlePhysEngineList[0];
|
||||
m_idlePhysEngineList.Remove(idleSEInfo);
|
||||
return idleSEInfo;
|
||||
}
|
||||
|
||||
#endregion Load balancing functions
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) Contributors, http://opensimulator.org/
|
||||
* See CONTRIBUTORS.TXT for a full list of copyright holders.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the OpenSim 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.
|
||||
*/
|
||||
|
||||
//KittyL: Added to support running script engine actor
|
||||
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
|
||||
namespace OpenSim.Region.Framework.Interfaces
|
||||
{
|
||||
//the interface for Scene to sync with Script Engine
|
||||
public interface IPhysEngineToSceneConnectorModule
|
||||
{
|
||||
bool Active { get; }
|
||||
bool Synced { get; }
|
||||
bool DebugWithViewer { get; }
|
||||
//void SendCoarseLocations();
|
||||
/// <summary>
|
||||
/// Update the property of prim with primID, where the prim is located at quark (LocX, LocY). The length of each quark is configurable.
|
||||
/// </summary>
|
||||
/// <param name="locX"></param>
|
||||
/// <param name="locY"></param>
|
||||
/// <param name="primID"></param>
|
||||
/// <param name="pName"></param>
|
||||
/// <param name="pValue"></param>
|
||||
void SendSetPrimProperties(uint locX, uint locY, UUID primID, string pName, object pValue);
|
||||
}
|
||||
}
|
|
@ -378,8 +378,6 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
|
||||
protected IScriptEngineToSceneConnectorModule m_scriptEngineToSceneConnectorModule;
|
||||
|
||||
|
||||
public IScriptEngineToSceneConnectorModule ScriptEngineToSceneConnectorModule
|
||||
{
|
||||
get { return m_scriptEngineToSceneConnectorModule; }
|
||||
|
@ -404,6 +402,48 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
return IsSyncedServer();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//RA: Physics Engine
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
protected IPhysEngineToSceneConnectorModule m_physEngineToSceneConnectorModule = null;
|
||||
public IPhysEngineToSceneConnectorModule PhysEngineToSceneConnectorModule
|
||||
{
|
||||
get { return m_physEngineToSceneConnectorModule; }
|
||||
set { m_physEngineToSceneConnectorModule = value; }
|
||||
}
|
||||
|
||||
// list of physactors for this scene so we can find them later for remote physics
|
||||
public Dictionary<uint, PhysicsActor> PhysActors = new Dictionary<uint, PhysicsActor>();
|
||||
public void AddPhysActor(uint id, PhysicsActor pActor)
|
||||
{
|
||||
if (PhysActors.ContainsKey(id)) {
|
||||
PhysActors.Remove(id);
|
||||
}
|
||||
PhysActors.Add(id, pActor);
|
||||
return;
|
||||
}
|
||||
public void RemovePhysActor(uint id)
|
||||
{
|
||||
if (PhysActors.ContainsKey(id)) {
|
||||
PhysActors.Remove(id);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public bool IsPhysEngineScene()
|
||||
{
|
||||
return (SceneToPhysEngineConnectorModule != null);
|
||||
}
|
||||
public bool IsActivePhysEngineScene()
|
||||
{
|
||||
return (SceneToPhysEngineConnectorModule != null && SceneToPhysEngineConnectorModule.Active);
|
||||
}
|
||||
public bool IsPhysEngineActor()
|
||||
{
|
||||
return (PhysEngineToSceneConnectorModule != null);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
//This function should only be called by an actor who's local Scene is just a cache of the authorative Scene.
|
||||
//If the object already exists, use the new copy to replace it.
|
||||
|
|
|
@ -997,6 +997,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
if (m_rootPart.PhysActor != null)
|
||||
{
|
||||
Scene.RemovePhysActor(m_rootPart.PhysActor.LocalID);
|
||||
m_scene.PhysicsScene.RemovePrim(m_rootPart.PhysActor);
|
||||
m_rootPart.PhysActor = null;
|
||||
}
|
||||
|
@ -1585,6 +1586,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
dupe.RootPart.PhysActor.IsPhysical);
|
||||
|
||||
dupe.RootPart.PhysActor.LocalID = dupe.RootPart.LocalId;
|
||||
// RA: Register physActor in scene
|
||||
dupe.RootPart.DoPhysicsPropertyUpdate(dupe.RootPart.PhysActor.IsPhysical, true);
|
||||
}
|
||||
|
||||
|
@ -2273,6 +2275,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
//if (linkPart.PhysActor != null)
|
||||
//{
|
||||
// m_scene.RemovePhysActor(linkPart.PhysActor.LocalID);
|
||||
// m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor);
|
||||
|
||||
//linkPart.PhysActor = null;
|
||||
|
@ -2387,6 +2390,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
if (linkPart.PhysActor != null)
|
||||
{
|
||||
m_scene.RemovePhysActor(linkPart.PhysActor.LocalID);
|
||||
m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor);
|
||||
}
|
||||
|
||||
|
|
|
@ -1492,6 +1492,8 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
PhysActor.SOPName = this.Name; // save object name and desc into the PhysActor so ODE internals know the joint/body info
|
||||
PhysActor.SOPDescription = this.Description;
|
||||
PhysActor.LocalID = LocalId;
|
||||
// RA: register PhysActor with the scene
|
||||
ParentGroup.Scene.AddPhysActor(LocalId, PhysActor);
|
||||
DoPhysicsPropertyUpdate(RigidBody, true);
|
||||
PhysActor.SetVolumeDetect(VolumeDetectActive ? 1 : 0);
|
||||
}
|
||||
|
@ -1754,6 +1756,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
|
||||
// If we're not what we're supposed to be in the physics scene, recreate ourselves.
|
||||
//ParentGroup.Scene.RemovePhysActor(PhysActor.LocalID);
|
||||
//m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor);
|
||||
/// that's not wholesome. Had to make Scene public
|
||||
//PhysActor = null;
|
||||
|
@ -4283,6 +4286,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
AddFlag(PrimFlags.Phantom);
|
||||
if (PhysActor != null)
|
||||
{
|
||||
ParentGroup.Scene.RemovePhysActor(PhysActor.LocalID);
|
||||
m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor);
|
||||
/// that's not wholesome. Had to make Scene public
|
||||
PhysActor = null;
|
||||
|
@ -4308,6 +4312,8 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
if (pa != null)
|
||||
{
|
||||
pa.LocalID = LocalId;
|
||||
// RA: register PhysActor with scene
|
||||
ParentGroup.Scene.AddPhysActor(LocalId, PhysActor);
|
||||
DoPhysicsPropertyUpdate(UsePhysics, true);
|
||||
if (m_parentGroup != null)
|
||||
{
|
||||
|
|
|
@ -142,7 +142,8 @@ namespace OpenSim.Region.Physics.Manager
|
|||
|
||||
public abstract PrimitiveBaseShape Shape { set; }
|
||||
|
||||
public abstract uint LocalID { set; }
|
||||
// RA: used to be abstract but changed to allow 'get' without changing all the phys engines
|
||||
public virtual uint LocalID { set { return; } get { return 0; } }
|
||||
|
||||
public abstract bool Grabbed { set; }
|
||||
|
||||
|
@ -280,6 +281,7 @@ namespace OpenSim.Region.Physics.Manager
|
|||
public override uint LocalID
|
||||
{
|
||||
set { return; }
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public override bool Grabbed
|
||||
|
|
|
@ -2766,6 +2766,7 @@ namespace OpenSim.Region.Physics.OdePlugin
|
|||
//Watchdog.UpdateThread();
|
||||
}
|
||||
|
||||
#region Ninja Joints
|
||||
if (SupportsNINJAJoints)
|
||||
{
|
||||
// Create pending joints, if possible
|
||||
|
@ -2947,6 +2948,7 @@ namespace OpenSim.Region.Physics.OdePlugin
|
|||
}
|
||||
}
|
||||
}
|
||||
#endregion Ninja Joints
|
||||
|
||||
if (processedtaints)
|
||||
//Console.WriteLine("Simulate calls Clear of _taintedPrim list");
|
||||
|
|
|
@ -86,31 +86,32 @@ public class PECharacter : PhysicsActor
|
|||
public override Vector3 Size {
|
||||
get { return _size; }
|
||||
set { _size = value;
|
||||
m_log.Debug("[PEE] PEChar set Size");
|
||||
m_log.Debug("[RPE] PEChar set Size");
|
||||
Prop.Set(_localID, PropType.Size, _size);
|
||||
}
|
||||
}
|
||||
public override PrimitiveBaseShape Shape {
|
||||
set { _pbs = value;
|
||||
m_log.Debug("[PEE] PEChar set Shape");
|
||||
m_log.Debug("[RPE] PEChar set Shape");
|
||||
Prop.Set(_localID, PropType.Shape, _pbs);
|
||||
}
|
||||
}
|
||||
public override uint LocalID {
|
||||
set { _localID = value;
|
||||
m_log.Debug("[PEE] PEChar set LocalID");
|
||||
m_log.Debug("[RPE] PEChar set LocalID");
|
||||
Prop.Set(_localID, PropType.LocalID, _localID);
|
||||
}
|
||||
get { return _localID; }
|
||||
}
|
||||
public override bool Grabbed {
|
||||
set { _grabbed = value;
|
||||
m_log.Debug("[PEE] PEChar set Grabbed");
|
||||
m_log.Debug("[RPE] PEChar set Grabbed");
|
||||
Prop.Set(_localID, PropType.Grabbed, _grabbed);
|
||||
}
|
||||
}
|
||||
public override bool Selected {
|
||||
set { _selected = value;
|
||||
m_log.Debug("[PEE] PEChar set Selected");
|
||||
m_log.Debug("[RPE] PEChar set Selected");
|
||||
Prop.Set(_localID, PropType.Selected, _selected);
|
||||
}
|
||||
}
|
||||
|
@ -122,7 +123,7 @@ public class PECharacter : PhysicsActor
|
|||
public override Vector3 Position {
|
||||
get { return _position; }
|
||||
set { _position = value;
|
||||
// m_log.Debug("[PEE] PEChar set Position");
|
||||
// m_log.Debug("[RPE] PEChar set Position");
|
||||
Prop.Set(_localID, PropType.Position, _position);
|
||||
}
|
||||
}
|
||||
|
@ -132,7 +133,7 @@ public class PECharacter : PhysicsActor
|
|||
public override Vector3 Force {
|
||||
get { return _force; }
|
||||
set { _force = value;
|
||||
m_log.Debug("[PEE] PEChar set Force");
|
||||
m_log.Debug("[RPE] PEChar set Force");
|
||||
Prop.Set(_localID, PropType.Force, _force);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ public class PEPlugin : IPhysicsPlugin
|
|||
|
||||
public string GetName()
|
||||
{
|
||||
return ("PhysicsEngineEngine");
|
||||
return ("RemotePhysicsEngine");
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
|
@ -84,31 +84,32 @@ public sealed class PEPrim : PhysicsActor
|
|||
public override Vector3 Size {
|
||||
get { return _size; }
|
||||
set { _size = value;
|
||||
m_log.Debug("[PEE] PEPrim set Size");
|
||||
m_log.Debug("[RPE] PEPrim set Size");
|
||||
Prop.Set(_localID, PropType.Size, _size);
|
||||
}
|
||||
}
|
||||
public override PrimitiveBaseShape Shape {
|
||||
set { _pbs = value;
|
||||
m_log.Debug("[PEE] PEPrim set Shape");
|
||||
m_log.Debug("[RPE] PEPrim set Shape");
|
||||
Prop.Set(_localID, PropType.Shape, _pbs);
|
||||
}
|
||||
}
|
||||
public override uint LocalID {
|
||||
set { _localID = value;
|
||||
m_log.Debug("[PEE] PEPrim set LocalID");
|
||||
m_log.Debug("[RPE] PEPrim set LocalID");
|
||||
Prop.Set(_localID, PropType.LocalID, _localID);
|
||||
}
|
||||
get { return _localID; }
|
||||
}
|
||||
public override bool Grabbed {
|
||||
set { _grabbed = value;
|
||||
m_log.Debug("[PEE] PEPrim set Grabbed");
|
||||
m_log.Debug("[RPE] PEPrim set Grabbed");
|
||||
Prop.Set(_localID, PropType.Grabbed, _grabbed);
|
||||
}
|
||||
}
|
||||
public override bool Selected {
|
||||
set { _selected = value;
|
||||
m_log.Debug("[PEE] PEPrim set Selected");
|
||||
m_log.Debug("[RPE] PEPrim set Selected");
|
||||
Prop.Set(_localID, PropType.Selected, _selected);
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +121,7 @@ public sealed class PEPrim : PhysicsActor
|
|||
public override Vector3 Position {
|
||||
get { return _position; }
|
||||
set { _position = value;
|
||||
m_log.Debug("[PEE] PEPrim set Position");
|
||||
m_log.Debug("[RPE] PEPrim set Position");
|
||||
Prop.Set(_localID, PropType.Position, _position);
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +131,7 @@ public sealed class PEPrim : PhysicsActor
|
|||
public override Vector3 Force {
|
||||
get { return _force; }
|
||||
set { _force = value;
|
||||
m_log.Debug("[PEE] PEPrim set Force");
|
||||
m_log.Debug("[RPE] PEPrim set Force");
|
||||
Prop.Set(_localID, PropType.Force, _force);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Nini.Config;
|
||||
using log4net;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Region.Physics.Manager;
|
||||
using OpenMetaverse;
|
||||
|
@ -34,6 +35,12 @@ namespace OpenSim.Region.Physics.PEPlugin
|
|||
{
|
||||
public class PEScene : PhysicsScene
|
||||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private List<PECharacter> m_avatars = new List<PECharacter>();
|
||||
private List<PEPrim> m_prims = new List<PEPrim>();
|
||||
private float[] m_heightMap;
|
||||
|
||||
public PEScene(string identifier)
|
||||
{
|
||||
}
|
||||
|
@ -46,15 +53,32 @@ public class PEScene : PhysicsScene
|
|||
{
|
||||
PECharacter actor = new PECharacter(avName, this, position, null, size, 0f, 0f, .5f, 1f,
|
||||
1f, 1f, .5f, .5f);
|
||||
m_avatars.Add(actor);
|
||||
return actor;
|
||||
}
|
||||
|
||||
public override void RemoveAvatar(PhysicsActor actor)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_avatars.Remove((PECharacter)actor);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.WarnFormat("[RPE]: Attempt to remove avatar that is not in physics scene: {0}", e);
|
||||
}
|
||||
}
|
||||
|
||||
public override void RemovePrim(PhysicsActor prim)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_prims.Remove((PEPrim)prim);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
m_log.WarnFormat("[RPE]: Attempt to remove prim that is not in physics scene: {0}", e);
|
||||
}
|
||||
}
|
||||
|
||||
public override PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, Vector3 position,
|
||||
|
@ -66,6 +90,7 @@ public class PEScene : PhysicsScene
|
|||
Vector3 size, Quaternion rotation, bool isPhysical)
|
||||
{
|
||||
PEPrim prim = new PEPrim(primName, this, position, size, rotation, null, pbs, isPhysical, null);
|
||||
m_prims.Add(prim);
|
||||
return prim;
|
||||
}
|
||||
|
||||
|
@ -73,12 +98,62 @@ public class PEScene : PhysicsScene
|
|||
|
||||
public override float Simulate(float timeStep)
|
||||
{
|
||||
foreach (PECharacter actor in m_avatars)
|
||||
{
|
||||
Vector3 actorPosition = actor.Position;
|
||||
Vector3 actorVelocity = actor.Velocity;
|
||||
|
||||
actorPosition.X += actor.Velocity.X*timeStep;
|
||||
actorPosition.Y += actor.Velocity.Y*timeStep;
|
||||
|
||||
actorPosition.Y = Math.Max(actorPosition.Y, 0.1f);
|
||||
actorPosition.Y = Math.Min(actorPosition.Y, Constants.RegionSize - 0.1f);
|
||||
actorPosition.X = Math.Max(actorPosition.X, 0.1f);
|
||||
actorPosition.X = Math.Min(actorPosition.X, Constants.RegionSize - 0.1f);
|
||||
|
||||
float height = 25.0F;
|
||||
try
|
||||
{
|
||||
height = m_heightMap[(int)actor.Position.Y * Constants.RegionSize + (int)actor.Position.X] + actor.Size.Z;
|
||||
}
|
||||
catch (OverflowException)
|
||||
{
|
||||
m_log.WarnFormat("[RPE]: Actor out of range: {0}", actor.SOPName, actor.Position.ToString());
|
||||
}
|
||||
|
||||
if (actor.Flying)
|
||||
{
|
||||
if (actor.Position.Z + (actor.Velocity.Z*timeStep) <
|
||||
m_heightMap[(int)actor.Position.Y * Constants.RegionSize + (int)actor.Position.X] + 2)
|
||||
{
|
||||
actorPosition.Z = height;
|
||||
actorVelocity.Z = 0;
|
||||
actor.IsColliding = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
actorPosition.Z += actor.Velocity.Z*timeStep;
|
||||
actor.IsColliding = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
actorPosition.Z = height;
|
||||
actorVelocity.Z = 0;
|
||||
actor.IsColliding = true;
|
||||
}
|
||||
|
||||
actor.Position = actorPosition;
|
||||
actor.Velocity = actorVelocity;
|
||||
}
|
||||
return 60f; // returns frames per second
|
||||
}
|
||||
|
||||
public override void GetResults() { }
|
||||
|
||||
public override void SetTerrain(float[] heightMap) { }
|
||||
public override void SetTerrain(float[] heightMap) {
|
||||
m_heightMap = heightMap;
|
||||
}
|
||||
|
||||
public override void SetWaterLevel(float baseheight) { }
|
||||
|
||||
|
|
Loading…
Reference in New Issue