Added missing files to git

dsg
Dan Lake 2010-09-27 16:00:29 -07:00
parent f5d25981ac
commit 32b9a8d78d
11 changed files with 3901 additions and 50 deletions

View File

@ -47,25 +47,37 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
public void Initialise(Scene scene, IConfigSource config)
{
/* This config parsing pattern should be used in each of these files:
OpenSim.cs
RegionSyncServerModule.cs
RegionSyncClientModule.cs
ScriptEngineToSceneConnectorModule.cs
*/
m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
IConfig syncConfig = config.Configs["RegionSyncModule"];
if (syncConfig == null || syncConfig.GetString("Mode", "client").ToLower() != "client")
m_active = false;
if (syncConfig == null)
m_log.Warn("[REGION SYNC CLIENT MODULE] No RegionSyncModule config section found. Shutting down.");
else if (!syncConfig.GetBoolean("Enabled", true))
m_log.Warn("[REGION SYNC CLIENT MODULE] RegionSyncModule is not enabled. Shutting down.");
else if (!syncConfig.GetString("Mode", "client").ToLower().Equals("client"))
m_log.WarnFormat("[REGION SYNC CLIENT MODULE] RegionSyncModule is not in client mode. Shutting down.");
else
{
m_active = false;
m_log.Warn("[REGION SYNC CLIENT MODULE] Not in client mode. Shutting down.");
return;
m_scene = scene;
m_active = true;
m_scene.RegionSyncEnabled = true;
m_scene.RegionSyncMode = "client";
m_serveraddr = syncConfig.GetString("ServerIPAddress", "127.0.0.1");
m_serverport = syncConfig.GetInt("ServerPort", 13000);
m_scene.RegisterModuleInterface<IRegionSyncClientModule>(this);
// Setup the command line interface
m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole;
InstallInterfaces();
m_log.Warn("[REGION SYNC CLIENT MODULE] Initialised");
}
m_serveraddr = syncConfig.GetString("ServerIPAddress", "127.0.0.1");
m_serverport = syncConfig.GetInt("ServerPort", 13000);
m_scene = scene;
m_scene.RegisterModuleInterface<IRegionSyncClientModule>(this);
// Setup the command line interface
m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole;
InstallInterfaces();
m_log.Warn("[REGION SYNC CLIENT MODULE] Initialised");
}
public void PostInitialise()

View File

@ -0,0 +1,285 @@
using System;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using OpenMetaverse.StructuredData;
using log4net;
using OpenSim.Framework;
using OpenMetaverse;
namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
{
class RegionSyncUtil
{
// The logfile
private static ILog m_log;
//HashSet<string> exceptions = new HashSet<string>();
public static OSDMap DeserializeMessage(RegionSyncMessage msg, string logHeader)
{
OSDMap data = null;
try
{
data = OSDParser.DeserializeJson(Encoding.ASCII.GetString(msg.Data, 0, msg.Length)) as OSDMap;
}
catch (Exception e)
{
m_log.Error(logHeader + " " + Encoding.ASCII.GetString(msg.Data, 0, msg.Length));
data = null;
}
return data;
}
#region Quark operations
//Convert a list of quarks, each identified by "x_y", where (x,y) are the offset position (in a 256x256 scene) of the left-bottom corner, to a single string
public static string QuarkStringListToString(List<string> quarks)
{
string quarkString = "";
foreach (string quark in quarks)
{
quarkString += quark + ",";
}
//trim the last ','
char[] trimChar = {','};
return quarkString.TrimEnd(trimChar);
//return quarkString;
}
//Convert a list of quarks, each identified by a QuarkInfo data structure, to a single string
public static string QuarkInfoToString(List<QuarkInfo> quarks)
{
string quarkString = "";
foreach (QuarkInfo quark in quarks)
{
quarkString += quark.QuarkStringRepresentation + ",";
}
//trim the last ','
char[] trimChar = { ',' };
return quarkString.TrimEnd(trimChar);
//return quarkString;
}
public static List<string> QuarkStringToStringList(string quarkString)
{
string[] data = quarkString.Split(new char[] { ',' });
List<string> quarkList = new List<string>(data);
return quarkList;
}
//public static List<QuarkInfo> GetQuarkInfoList(List<string> quarkStringList, int quarkSizeX, int quarkSizeY)
public static List<QuarkInfo> GetQuarkInfoList(List<string> quarkStringList)
{
List<QuarkInfo> quarkInfoList = new List<QuarkInfo>();
foreach (string quarkString in quarkStringList)
{
QuarkInfo quark = new QuarkInfo(quarkString);
quarkInfoList.Add(quark);
}
return quarkInfoList;
}
//public static List<QuarkInfo> GetAllQuarksInScene(int QuarkInfo.SizeX, int QuarkInfo.SizeY)
public static List<QuarkInfo> GetAllQuarksInScene()
{
List<QuarkInfo> quarkList = new List<QuarkInfo>();
int xSlots =(int) Constants.RegionSize / QuarkInfo.SizeX;
int ySlots = (int) Constants.RegionSize / QuarkInfo.SizeY;
for (int i = 0; i < xSlots; i++)
{
int posX = i * QuarkInfo.SizeX;
for (int j = 0; j < ySlots; j++)
{
int posY = j * QuarkInfo.SizeY;
QuarkInfo quark = new QuarkInfo(posX, posY);
quarkList.Add(quark);
}
}
return quarkList;
}
//public static List<string> GetAllQuarkStringInScene(int QuarkInfo.SizeX, int QuarkInfo.SizeY)
public static List<string> GetAllQuarkStringInScene()
{
List<string> quarkStringList = new List<string>();
int xSlots = (int)Constants.RegionSize / QuarkInfo.SizeX;
int ySlots = (int)Constants.RegionSize / QuarkInfo.SizeY;
for (int i = 0; i < xSlots; i++)
{
int posX = i * QuarkInfo.SizeX;
for (int j = 0; j < ySlots; j++)
{
int posY = j * QuarkInfo.SizeY;
string quarkString = "";
quarkString += posX + "_" + posY;
quarkStringList.Add(quarkString);
}
}
return quarkStringList;
}
//public static string GetQuarkIDByPosition(Vector3 pos, int QuarkInfo.SizeX, int QuarkInfo.SizeY)
public static string GetQuarkIDByPosition(Vector3 pos)
{
float x, y;
if (pos.X < 0)
{
x = 0;
}
else if (pos.X >= Constants.RegionSize)
{
x = (int)Constants.RegionSize - 1;
}
else
x = pos.X;
if (pos.Y < 0)
{
y = 0;
}
else if (pos.Y >= Constants.RegionSize)
{
y = (int)Constants.RegionSize - 1;
}
else
y = pos.Y;
int xRange = (int) x / QuarkInfo.SizeX;
int yRange = (int) y / QuarkInfo.SizeY;
int quarkPosX = xRange * QuarkInfo.SizeX;
int quarkPosY = yRange * QuarkInfo.SizeY;
string qID = quarkPosX + "_" + quarkPosX;
return qID;
}
//public static List<QuarkInfo> GetQuarkSubscriptions(int QuarkInfo.SizeX, int QuarkInfo.SizeY, int xmin, int ymin, int xmax, int ymax)
public static List<QuarkInfo> GetQuarkSubscriptions(int xmin, int ymin, int xmax, int ymax)
{
List<QuarkInfo> quarkList = new List<QuarkInfo>();
int xStart = (int)xmin / QuarkInfo.SizeX;
int yStart = (int)ymin / QuarkInfo.SizeY;
int xEnd = (int)xmax / QuarkInfo.SizeX;
int yEnd = (int)ymax / QuarkInfo.SizeY;
for (int i = xStart; i < xEnd; i++)
{
int posX = i * QuarkInfo.SizeX;
for (int j = yStart; j < yEnd; j++)
{
int posY = j * QuarkInfo.SizeY;
QuarkInfo quark = new QuarkInfo(posX, posY);
quarkList.Add(quark);
}
}
return quarkList;
}
public static int[] GetCornerCoordinates(string space)
{
if (space == "") return null;
string[] corners = space.Split(new char[] { ',' });
string leftBottom = corners[0];
string rightTop = corners[1];
string[] coordinates = leftBottom.Split(new char[] { '_' });
int [] results = new int[4];
results[0] = Convert.ToInt32(coordinates[0]);
results[1] = Convert.ToInt32(coordinates[1]);
coordinates = rightTop.Split(new char[] { '_' });
results[2] = Convert.ToInt32(coordinates[0]);
results[3] = Convert.ToInt32(coordinates[1]);
return results;
}
public static string GetSpaceStringRepresentationByCorners(int minX, int minY, int maxX, int maxY)
{
string spaceString = minX + "_" + minY+","+maxX+"_"+maxY;
return spaceString;
}
public static Dictionary<string, int[]> BinarySpaceParition(int minX, int minY, int maxX, int maxY)
{
int xLen = maxX - minX;
int yLen = maxY - minY;
//We always return the half space that share the same right-top corner, (maxX, maxY), with the original space.
int upperPlainStartX, upperPlainStartY;
int upperPlainEndX = maxX, upperPlainEndY = maxY;
bool partitionOnX;
if (xLen >= yLen)
{
//partition along x axis
partitionOnX = true;
int partXLen = xLen / 2;
upperPlainStartX = minX + partXLen;
upperPlainStartY = minY;
}
else
{
//partition along y axis
partitionOnX = false;
int partYLen = yLen / 2;
upperPlainStartY = minY + partYLen;
upperPlainStartX = minX;
}
Dictionary<string, int[]> partitionedSpace = new Dictionary<string, int[]>();
//upper plain refers to the right (if partition along x) or top (if partition along y) part
int[] upperPlain = new int[4];
upperPlain[0] = upperPlainStartX;
upperPlain[1] = upperPlainStartY;
upperPlain[2] = upperPlainEndX;
upperPlain[3] = upperPlainEndY;
int[] lowerPlain = new int[4];
if (partitionOnX)
{
lowerPlain[0] = minX;
lowerPlain[1] = minY;
lowerPlain[2] = upperPlainStartX;
lowerPlain[3] = maxY;
}
else
{
lowerPlain[0] = minX;
lowerPlain[1] = minY;
lowerPlain[2] = maxX;
lowerPlain[3] = upperPlainStartY;
}
partitionedSpace.Add("lower", lowerPlain);
partitionedSpace.Add("upper", upperPlain);
return partitionedSpace;
}
public static int[] RemoveSpace(int originMinX, int originMinY, int originMaxX, int originMaxY, int toRemoveMinX, int toRemoveMinY, int toRemoveMaxX, int toRemoveMaxY)
{
int[] remainingBox = new int[4];
remainingBox[0] = originMinX;
remainingBox[1] = originMinY;
if (originMinX == toRemoveMinX)
{
//partitioned along Y
remainingBox[2] = originMaxX;
remainingBox[3] = toRemoveMinY;
}
else
{
//partitioned along X
remainingBox[2] = toRemoveMinX;
remainingBox[3] = originMaxY;
}
return remainingBox;
}
#endregion Quark operations
}
}

View File

@ -0,0 +1,640 @@
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 SceneToScriptEngineConnector acts as a thread on the RegionSyncServer to handle incoming
// messages from ScriptEngineToSceneConnectors that run on Script Engines. It connects the
// authoratative Scene with remote script engines.
public class SceneToScriptEngineConnector
{
#region SceneToScriptEngineConnector 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 SceneToScriptEngineSyncServer 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("[SceneToScriptEngineConnector #{0}]", m_connection_number);
return String.Format("[SceneToScriptEngineConnector #{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("RegionSyncScriptAPI #{0}", m_connection_number);
return String.Format("RegionSyncScriptAPI #{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 SceneToScriptEngineConnector(int num, Scene scene, TcpClient client, SceneToScriptEngineSyncServer 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 RegionSyncScriptEngine
public void Shutdown()
{
m_syncServer.RemoveSyncedScriptEngine(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 Script 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.QuarkSubscription:
HandleQuarkSubscription(msg);
return;
case RegionSyncMessage.MsgType.RegionName:
{
m_regionName = Encoding.ASCII.GetString(msg.Data, 0, msg.Length);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.RegionName, m_scene.RegionInfo.RegionName));
RegionSyncMessage.HandleSuccess(LogHeader, msg, String.Format("Syncing to region \"{0}\"", m_regionName));
return;
}
case RegionSyncMessage.MsgType.GetTerrain:
{
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.Terrain, m_scene.Heightmap.SaveToXmlString()));
RegionSyncMessage.HandleSuccess(LogHeader, msg, "Terrain sent");
return;
}
case RegionSyncMessage.MsgType.GetObjects:
{
//List<EntityBase> entities = m_scene.GetEntities();
//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.
List<SceneObjectGroup> objectsInSpace = GetObjectsInGivenSpace(m_scene, m_quarkSubscriptions);
foreach (SceneObjectGroup sog in objectsInSpace)
{
Send(PrepareObjectUpdateMessage(RegionSyncMessage.MsgType.NewObject, sog));
}
RegionSyncMessage.HandleSuccess(LogHeader, msg, "Sent all scene objects");
return;
}
case RegionSyncMessage.MsgType.SetObjectProperty:
{
OSDMap data = RegionSyncUtil.DeserializeMessage(msg, LogHeader);
if (!data.ContainsKey("UUID") || !data.ContainsKey("name"))
{
m_log.WarnFormat("{0} Parameters missing in SetObjectProperty request, need \"UUID\", \"name\" (property-name), and \"valParams\" (property-value)", LogHeader);
return;
}
UUID objID = data["UUID"].AsUUID();
string pName = data["name"].AsString();
//valParams
SetObjectProperty(objID, pName, data);
}
return;
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.AddSyncedScriptEngine(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 ScriptEngine 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} Script Engine has disconnected.", LogHeader);
}
}
}
public void SendObjectUpdate(RegionSyncMessage.MsgType msgType, SceneObjectGroup sog)
{
Send(PrepareObjectUpdateMessage(msgType, sog));
}
#endregion Send/Receive messages to/from the remote Script Engine
#region stub functions for remote actors to set object properties
//NOTE: the current implementation of setting various object properties is mimicking the way script engine sets object properties
// (i.e. as implemented in LSL_Api.cs. But the function calls are meant to be generic for all actors. We may need to change
// the implementation details later to accomdate more actors, and move it to the Scene implementation.
//TODO: These functions should eventually be part of DSG interface, rather than part of SceneToScriptEngineConnector.
private void SetObjectProperty(UUID primID, string pName, OSDMap valParams)
{
//m_log.Debug(LogHeader + " received SetObjectProperty request, " + primID + ", " + pName);
switch (pName)
{
case "object_rez":
//rez a new object, rather than update an existing object's property
RezObjectOnScene(primID, valParams);
break;
case "color":
SetPrimColor(primID, valParams);
break;
case "pos":
SetPrimPos(primID, valParams);
break;
default:
break;
}
}
//Triggered when an object is rez'ed by script. Followed the implementation in LSL_Api.llRezAtRoot().
//TODO: the real rez object part should be executed by Scene class, not here
private void RezObjectOnScene(UUID primID, OSDMap data)
{
if(data["inventory"] == null || data["pos"]==null || data["vel"] == null || data["rot"]==null || data["param"]==null){
m_log.Warn(LogHeader + ": not enough parameters for setting color on " + primID);
return;
}
string inventory = data["inventory"].AsString();
Vector3 pos = data["pos"].AsVector3();
Vector3 vel = data["vel"].AsVector3();
Quaternion rot = data["rot"].AsQuaternion();
int param = data["param"].AsInteger();
SceneObjectPart primToFetchObject = m_scene.GetSceneObjectPart(primID);
if (primToFetchObject == null)
return;
TaskInventoryDictionary partInventory = (TaskInventoryDictionary)primToFetchObject.TaskInventory.Clone();
foreach (KeyValuePair<UUID, TaskInventoryItem> inv in partInventory)
{
if (inv.Value.Name == inventory)
{
// make sure we're an object.
if (inv.Value.InvType != (int)InventoryType.Object)
{
//llSay(0, "Unable to create requested object. Object is missing from database.");
m_log.Warn(LogHeader + ": Unable to create requested object. Object is missing from database.");
return;
}
Vector3 llpos = pos;
Vector3 llvel = vel;
// need the magnitude later
float velmag = (float)Util.GetMagnitude(llvel);
SceneObjectGroup new_group = m_scene.RezObject(primToFetchObject, inv.Value, llpos, rot, llvel, param);
// If either of these are null, then there was an unknown error.
if (new_group == null)
continue;
if (new_group.RootPart == null)
continue;
// objects rezzed with this method are die_at_edge by default.
new_group.RootPart.SetDieAtEdge(true);
/*
m_ScriptEngine.PostObjectEvent(m_host.LocalId, new EventParams(
"object_rez", new Object[] {
new LSL_String(
new_group.RootPart.UUID.ToString()) },
new DetectParams[0]));
* */
//NOTE: should replace the above code with the following and implement TriggerRezObject. However, since
// nothing is really doen with "object_rez" in XEngine, we ignore posting the event for now.
//m_scene.EventManager.TriggerRezObject();
float groupmass = new_group.GetMass();
if (new_group.RootPart.PhysActor != null && new_group.RootPart.PhysActor.IsPhysical && llvel != Vector3.Zero)
{
//Recoil.
//llApplyImpulse(new LSL_Vector(llvel.X * groupmass, llvel.Y * groupmass, llvel.Z * groupmass), 0);
//copy the implementation of llApplyImpulse here.
Vector3 v = new Vector3(llvel.X * groupmass, llvel.Y * groupmass, llvel.Z * groupmass);
if (v.Length() > 20000.0f)
{
v.Normalize();
v = v * 20000.0f;
}
new_group.RootPart.ApplyImpulse(v, false);
}
return;
}
}
}
private void SetPrimColor(UUID primID, OSDMap data)
{
if (!data.ContainsKey("color") || !data.ContainsKey("face"))
{
m_log.Warn(LogHeader + ": not enough parameters for setting color on " + primID);
return;
}
SceneObjectPart primToUpdate = m_scene.GetSceneObjectPart(primID);
if (primToUpdate == null)
return;
Vector3 color = data["color"].AsVector3();
int face = data["face"].AsInteger();
//m_log.DebugFormat("{0}: Set Color request, to set ({1}) on face {2}", LogHeader, color.ToString(), face);
primToUpdate.SetFaceColor(color, face);
}
//Whichever actor that triggers this function call shall have already checked the legitimacy of the position.
private void SetPrimPos(UUID primID, OSDMap data)
{
if (!data.ContainsKey("pos"))
{
m_log.Warn(LogHeader + ": not enough parameters for setting pos on " + primID);
return;
}
Vector3 pos = data["pos"].AsVector3();
SceneObjectPart primToUpdate = m_scene.GetSceneObjectPart(primID);
if (primToUpdate == null)
return;
SceneObjectGroup parent = primToUpdate.ParentGroup;
if (parent.RootPart == primToUpdate)
{
//the prim is the root-part of the object group, set the position of the whole group
parent.UpdateGroupPosition(pos);
}
else
{
//the prim is not the root-part, set the offset position
primToUpdate.OffsetPosition = pos;
parent.HasGroupChanged = true;
parent.ScheduleGroupForTerseUpdate();
}
}
#endregion
#region Event Handlers
//calling by SceneToSESyncServer
public void SEConnectorOnRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource)
{
m_log.Debug(LogHeader + ": Caught event OnRezScript, send message to Script Engine");
OSDMap data = new OSDMap(7);
data["localID"] = OSD.FromUInteger(localID);
data["itemID"] = OSD.FromUUID(itemID);
data["script"] = OSD.FromString(script);
data["startParam"] = OSD.FromInteger(startParam);
data["postOnRez"] = OSD.FromBoolean(postOnRez);
data["engine"] = OSD.FromString(engine);
data["stateSource"] = OSD.FromInteger(stateSource);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.OnRezScript, OSDParser.SerializeJsonString(data)));
}
public void SEConnectorOnScriptReset(uint localID, UUID itemID)
{
m_log.Debug(LogHeader + ": Caught event OnScriptReset, send message to Script Engine");
OSDMap data = new OSDMap(2);
data["localID"] = OSD.FromUInteger(localID);
data["itemID"] = OSD.FromUUID(itemID);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.OnScriptReset, OSDParser.SerializeJsonString(data)));
}
public void SEConnectorOnUpdateScript(UUID agentID, UUID itemId, UUID primId, bool isScriptRunning, UUID newAssetID)
{
m_log.Debug(LogHeader + ": Caught event OnUpdateScript, send message to Script Engine");
OSDMap data = new OSDMap(5);
data["agentID"] = OSD.FromUUID(agentID);
data["itemID"] = OSD.FromUUID(itemId);
data["primID"] = OSD.FromUUID(primId);
data["running"] = OSD.FromBoolean(isScriptRunning);
data["assetID"] = OSD.FromUUID(newAssetID);
Send(new RegionSyncMessage(RegionSyncMessage.MsgType.OnUpdateScript, OSDParser.SerializeJsonString(data)));
}
#endregion Event Handlers
#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
}
}

View File

@ -0,0 +1,605 @@
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
{
public class QuarkInfo
{
public static int SizeX = -1; //the length along X axis, should be same for all quarks, need to be set by either the SyncServer on Scene, or by an ActorToSceneConnectorModule.
public static int SizeY = -1; //the length along Y axis, should be same for all quarks
public int PosX = 0; //the offset position of the left-bottom corner (0~255)
public int PosY = 0;
private ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private int maxX, maxY;
//this field is only meaning for the QuarkInfo records on the Scene side
private SceneToScriptEngineConnector m_seConnector=null;
public SceneToScriptEngineConnector SEConnector
{
get { return m_seConnector; }
set { m_seConnector = value; }
}
private string m_quarkString = "";
public string QuarkStringRepresentation
{
get { return m_quarkString;}
}
//public QuarkInfo(int x, int y, int sizeX, int sizeY)
public QuarkInfo(int x, int y)
{
PosX = x;
PosY = y;
//SizeX = sizeX;
//SizeY = sizeY;
maxX = PosX + SizeX;
maxY = PosY + SizeY;
m_quarkString = PosX + "_" + PosY;
}
//public QuarkInfo(string xy, int sizeX, int sizeY)
public QuarkInfo(string xy)
{
string[] coordinate = xy.Split(new char[] { '_' });
if (coordinate.Length < 2)
{
m_log.Warn("[QUARK INFO] QuarkInfo Constructor: missing x or y value, format expected x_y: " + xy);
return;
}
PosX = Convert.ToInt32(coordinate[0]);
PosY = Convert.ToInt32(coordinate[1]);
//SizeX = sizeX;
//SizeY = sizeY;
maxX = PosX + SizeX;
maxY = PosY + SizeY;
m_quarkString = PosX + "_" + PosY;
}
//public void SetPartitionedSceneInfo(int x, int y, int sizeX, int sizeY)
public void SetPartitionedSceneInfo(int x, int y)
{
PosX = x;
PosY = y;
//SizeX = sizeX;
//SizeY = sizeY;
maxX = PosX + SizeX;
maxY = PosY + SizeY;
m_quarkString = PosX + "_" + PosY;
}
public bool IsPositionInQuarkSpace(Vector3 pos)
{
if (pos.X >= PosX && pos.X <= maxX && pos.Y >= PosY && pos.Y <= maxY)
return true;
else
return false;
}
}
//Information of a registered idle script engine.
//Note, this is a temporary solution to inlcude idle script engines here.
//In the future, there might be a independent load balaner that keeps track
//of available idle hardware.
public class IdleScriptEngineInfo
{
public TcpClient TClient;
//public IPAddress ScriptEngineIPAddr;
//public int ScriptEnginePort;
public string ID;
//Will be used to store the overloaded SE that has send LB request and paired with this idle SE
public SceneToScriptEngineConnector AwaitOverloadedSE=null;
public IdleScriptEngineInfo(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 Script Engines.
public class SceneToScriptEngineSyncServer
{
#region SceneToScriptEngineSyncServer members
// Set the addr and port for TcpListener
private IPAddress m_addr;
private Int32 m_port;
private int seCounter;
// 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_scriptEngineConnector_lock = new object();
//private Dictionary<string, SceneToScriptEngineConnector> m_scriptEngineConnectors = new Dictionary<string, SceneToScriptEngineConnector>();
private List<SceneToScriptEngineConnector> m_scriptEngineConnectors = new List<SceneToScriptEngineConnector>();
//list of idle script engines that have registered.
private List<IdleScriptEngineInfo> m_idleScriptEngineList = new List<IdleScriptEngineInfo>();
//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 SCRIPT 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_scriptEngineConnectors.Count > 0);
}
}
#endregion
// Constructor
public SceneToScriptEngineSyncServer(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()
{
m_scene.EventManager.OnRezScript += SESyncServerOnRezScript;
m_scene.EventManager.OnScriptReset += SESyncServerOnScriptReset;
m_scene.EventManager.OnUpdateScript += SESyncServerOnUpdateScript;
}
private void UnSubscribeToEvents()
{
m_scene.EventManager.OnRezScript -= SESyncServerOnRezScript;
m_scene.EventManager.OnScriptReset -= SESyncServerOnScriptReset;
m_scene.EventManager.OnUpdateScript -= SESyncServerOnUpdateScript;
}
// Start the server
public void Start()
{
m_listenerThread = new Thread(new ThreadStart(Listen));
m_listenerThread.Name = "SceneToScriptEngineSyncServer 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 (SceneToScriptEngineConnector seConnector in m_scriptEngineConnectors)
{
seConnector.Shutdown();
}
m_scriptEngineConnectors.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, SceneToScriptEngineConnector seConnector)
{
foreach (QuarkInfo quark in quarkSubscriptions)
{
string quarkID = quark.QuarkStringRepresentation;
m_quarksInScene[quarkID].SEConnector = seConnector;
m_log.Debug(LogHeader + ": " + quarkID + " subscribed by "+seConnector.Description);
}
}
// Add a connector to a script engine
public void AddSyncedScriptEngine(SceneToScriptEngineConnector seConnector)
{
lock (m_scriptEngineConnector_lock)
{
//Dictionary<string, SceneToScriptEngineConnector> currentlist = m_scriptEngineConnectors;
//Dictionary<string, SceneToScriptEngineConnector> newlist = new Dictionary<string, SceneToScriptEngineConnector>(currentlist);
m_scriptEngineConnectors.Add(seConnector);
// 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_scriptEngineConnectors = newlist;
}
}
// Remove the client view from the list and decrement synced client counter
public void RemoveSyncedScriptEngine(SceneToScriptEngineConnector seConnector)
{
lock (m_scriptEngineConnector_lock)
{
//Dictionary<string, SceneToScriptEngineConnector> currentlist = m_scriptEngineConnectors;
//Dictionary<string, SceneToScriptEngineConnector> newlist = new Dictionary<string, SceneToScriptEngineConnector>(currentlist);
m_scriptEngineConnectors.Remove(seConnector);
// 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_scriptEngineConnectors = 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 SceneToScriptEngineConnector to the list
SceneToScriptEngineConnector sceneToSEConnector = new SceneToScriptEngineConnector(++seCounter, m_scene, tcpclient, this);
AddSyncedScriptEngine(sceneToSEConnector);
break;
case ActorStatus.Idle:
IdleScriptEngineInfo idleSE = new IdleScriptEngineInfo(tcpclient);
m_log.Debug(": adding an idle SE ("+addr+","+port+")");
m_idleScriptEngineList.Add(idleSE);
break;
default:
break;
}
}
}
catch (SocketException e)
{
m_log.WarnFormat(LogHeader + " [Listen] SocketException: {0}", e);
}
}
/*
public void RegisterSyncedScriptEngine(SceneToScriptEngineConnector sceneToSEConnector)
{
//first, remove it from the idle list
m_idleScriptEngineList.Remove(sceneToSEConnector);
//now, added to the synced SE list
AddSyncedScriptEngine(sceneToSEConnector);
}
* */
// Broadcast a message to all connected RegionSyncClients
public void SendToAllConnectedSE(RegionSyncMessage msg)
{
if (m_scriptEngineConnectors.Count > 0)
{
m_log.Debug(LogHeader + ": region " + m_scene.RegionInfo.RegionName + " Broadcast to ScriptEngine, msg " + msg.Type);
foreach (SceneToScriptEngineConnector seConnector in m_scriptEngineConnectors)
{
seConnector.Send(msg);
}
}
}
//TO FINISH: Find the right SceneToSEConnector to forward the message
public void SendToSE(RegionSyncMessage.MsgType msgType, SceneObjectGroup sog)
{
SceneToScriptEngineConnector seConnector = GetSceneToSEConnector(sog);
if (seConnector != null)
{
seConnector.SendObjectUpdate(msgType, sog);
}
else
{
m_log.Warn(LogHeader + sog.AbsolutePosition.ToString() + " not covered by any script engine");
}
}
//This is to send a message, rsm, to script engine, and the message is about object SOG. E.g. RemovedObject
public void SendToSE(RegionSyncMessage rsm, SceneObjectGroup sog)
{
SceneToScriptEngineConnector seConnector = GetSceneToSEConnector(sog);
if (seConnector != null)
{
seConnector.Send(rsm);
}
else
{
m_log.Warn(LogHeader + sog.AbsolutePosition.ToString() + " not covered by any script engine");
}
}
private SceneToScriptEngineConnector GetSceneToSEConnector(SceneObjectGroup sog)
{
if (sog==null)
{
return m_scriptEngineConnectors[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);
SceneToScriptEngineConnector seConnector = m_quarksInScene[quarkID].SEConnector;
return seConnector;
}
}
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
private void SESyncServerOnRezScript(uint localID, UUID itemID, string script, int startParam, bool postOnRez, string engine, int stateSource)
{
SceneObjectGroup sog = m_scene.GetSceneObjectPart(localID).ParentGroup;
if (sog != null)
{
//First, figure out which script engine to forward the event
SceneToScriptEngineConnector seConnector = GetSceneToSEConnector(sog);
if (seConnector != null)
{
m_log.Debug(LogHeader + ": Caught event OnRezScript, send message to Script Engine " + seConnector.Description);
seConnector.SEConnectorOnRezScript(localID, itemID, script, startParam, postOnRez, engine, stateSource);
}
}
}
private void SESyncServerOnScriptReset(uint localID, UUID itemID)
{
SceneObjectGroup sog = m_scene.GetSceneObjectPart(localID).ParentGroup;
if (sog != null)
{
//First, figure out which script engine to forward the event
SceneToScriptEngineConnector seConnector = GetSceneToSEConnector(sog);
if (seConnector != null)
{
m_log.Debug(LogHeader + ": Caught event OnScriptReset, send message to Script Engine " + seConnector.Description);
seConnector.SEConnectorOnScriptReset(localID, itemID);
}
}
}
private void SESyncServerOnUpdateScript(UUID agentID, UUID itemId, UUID primId, bool isScriptRunning, UUID newAssetID)
{
SceneObjectGroup sog = m_scene.GetSceneObjectPart(primId).ParentGroup;
if (sog != null)
{
//First, figure out which script engine to forward the event
SceneToScriptEngineConnector seConnector = GetSceneToSEConnector(sog);
if (seConnector != null)
{
m_log.Debug(LogHeader + ": Caught event OnScriptReset, send message to Script Engine " + seConnector.Description);
seConnector.SEConnectorOnUpdateScript(agentID, itemId, primId, isScriptRunning, newAssetID);
}
}
}
#endregion Event Handlers
#region Load balancing members and functions
//keep track of idle script engines that are in the process of load balancing (they are off the idle list, but not a working script engine yet (not sync'ing with Scene yet)).
private Dictionary<string, IdleScriptEngineInfo> m_loadBalancingIdleSEs = new Dictionary<string,IdleScriptEngineInfo>();
public void HandleLoadBalanceRequest(SceneToScriptEngineConnector 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)
{
SceneToScriptEngineConnector seConnctor = (SceneToScriptEngineConnector)arg;
IdleScriptEngineInfo idleScriptEngineInfo = GetIdleScriptEngineConnector();
if (idleScriptEngineInfo != null)
{
RegionSyncMessage msg = new RegionSyncMessage(RegionSyncMessage.MsgType.LoadMigrationNotice);
Send(idleScriptEngineInfo.TClient, msg.ToBytes());
m_log.Debug(LogHeader + ": HandleLoadBalanceRequest from " + seConnctor.Description + ", picked idle SE: " + idleScriptEngineInfo.ID);
//keep track of which overload script engine is paired up with which idle script engine
idleScriptEngineInfo.AwaitOverloadedSE = seConnctor;
m_loadBalancingIdleSEs.Add(idleScriptEngineInfo.ID, idleScriptEngineInfo);
m_log.Debug("ToSEConnector portal: local -" +
((IPEndPoint)idleScriptEngineInfo.TClient.Client.LocalEndPoint).Address.ToString() + ":" + ((IPEndPoint)idleScriptEngineInfo.TClient.Client.LocalEndPoint).Port
+ "; remote - " + ((IPEndPoint)idleScriptEngineInfo.TClient.Client.RemoteEndPoint).Address.ToString() + ":"
+ ((IPEndPoint)idleScriptEngineInfo.TClient.Client.RemoteEndPoint).Port);
//Now we expect the idle script engine to reply back
msg = new RegionSyncMessage(idleScriptEngineInfo.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 script 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 script engine
seConnctor.Send(new RegionSyncMessage(RegionSyncMessage.MsgType.LoadBalanceResponse, OSDParser.SerializeJsonString(data)));
m_log.Debug(LogHeader + " now remove script engine " + idleScriptEngineInfo.ID + " from idle SE list, and create SceneToScriptEngineConnector to it");
//create a SceneToSEConnector for the idle script engine, who will be sync'ing with this SyncServer soon
SceneToScriptEngineConnector sceneToSEConnector = new SceneToScriptEngineConnector(++seCounter, m_scene, idleScriptEngineInfo.TClient, this);
//Now remove the script engine from the idle SE list
m_idleScriptEngineList.Remove(idleScriptEngineInfo);
//AddSyncedScriptEngine(sceneToSEConnector);
}
}
else
{
seConnctor.SendLoadBalanceRejection("no idle script 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} Script Engine has disconnected.", LogHeader);
}
}
}
private IdleScriptEngineInfo GetIdleScriptEngineConnector()
{
if (m_idleScriptEngineList.Count == 0)
return null;
IdleScriptEngineInfo idleSEInfo = m_idleScriptEngineList[0];
m_idleScriptEngineList.Remove(idleSEInfo);
return idleSEInfo;
}
#endregion Load balancing functions
}
}

View File

@ -0,0 +1,821 @@
/*
* 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 information of the authoratative copy of a scene
public class AuthSceneInfo
{
public string Name;
public string Addr;
public int Port;
public int LocX=-1;
public int LocY=-1;
public AuthSceneInfo(string name, string addr, int port)
{
Name = name;
Addr = addr;
Port = port;
}
public AuthSceneInfo(string name, string addr, int port, int locX, int locY)
{
Name = name;
Addr = addr;
Port = port;
LocX = locX;
LocY = locY;
}
}
//The connector that connects the local Scene (cache) and remote authoratative Scene
public class ScriptEngineToSceneConnectorModule : IRegionModule, IScriptEngineToSceneConnectorModule, 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 != "script_engine")
{
m_log.Warn("[REGION SYNC SCRIPT 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("ValidScriptEngineScene", "");
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 SEToSceneConnectorModule, 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 + "_SceneToSESyncServerIP";
string addr = syncConfig.GetString(serverAddr, "127.0.0.1");
string serverPort = authSceneName + "_SceneToSESyncServerPort";
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<IScriptEngineToSceneConnectorModule>(this);
m_syncConfig = syncConfig;
m_debugWithViewer = syncConfig.GetBoolean("ScriptEngineDebugWithViewer", 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 SCRIPT 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 "RegionSyncScriptEngineModule"; }
}
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 IScriptEngineToSceneConnectorModule members
public bool Active
{
get { return m_active; }
}
public bool Synced
{
get
{
lock(m_client_lock)
{
//return (m_scriptEngineToSceneConnector != null);
return (m_SEToSceneConnectors.Count > 0);
}
}
}
#endregion
#region ScriptEngineToSceneConnectorModule 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 ScriptEngineToSceneConnector 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, ScriptEngineToSceneConnector> m_SEToSceneConnectors = new Dictionary<string, ScriptEngineToSceneConnector>(); //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 = "[ScriptEngineToSceneConnectorModule]";
private ScriptEngineToSceneConnector m_idleSEToSceneConnector = 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 ScriptEngine 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;
ScriptEngineToSceneConnector connector = GetSEToSceneConnector(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 ScriptEngineToSceneConnector, given the location of the authoritative scene
private ScriptEngineToSceneConnector GetSEToSceneConnector(uint locX, uint locY)
{
string loc = SceneLocToString(locX, locY);
if (!m_authScenesInfoByLoc.ContainsKey(loc))
return null;
string authSceneName = m_authScenesInfoByLoc[loc].Name;
if (!m_SEToSceneConnectors.ContainsKey(authSceneName))
{
return null;
}
return m_SEToSceneConnectors[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 ScriptEngineToSceneConnectorModule 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_SEToSceneConnectors.Count>0)
{
string authScenes = "";
foreach (KeyValuePair<string, ScriptEngineToSceneConnector> valPair in m_SEToSceneConnectors)
{
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 RegionSyncScriptEngine");
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
ScriptEngineToSceneConnector scriptEngineToSceneConnector = new ScriptEngineToSceneConnector(m_scene, authSceneInfo.Addr, authSceneInfo.Port, m_debugWithViewer, authSceneName, m_syncConfig);
if (scriptEngineToSceneConnector.Start())
{
m_SEToSceneConnectors.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);
//InitScriptEngineToSceneConnector(quarkStringList);
InitScriptEngineToSceneConnector(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_idleSEToSceneConnector = new ScriptEngineToSceneConnector(m_scene, authSceneInfo.Addr, authSceneInfo.Port, m_debugWithViewer, authSceneName, m_syncConfig);
m_idleSEToSceneConnector.RegisterIdle();
}
/// <summary>
/// The given ScriptEngineToSceneConnector, 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(ScriptEngineToSceneConnector 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_SEToSceneConnectors.ContainsKey(authSceneName))
{
m_log.Warn(LogHeader + ": Connector to " + authSceneName + " is already considered connected");
return;
}
m_SEToSceneConnectors.Add(authSceneName, seToSceneConnector);
//there should only be one element in the dictionary if we reach this loop, anyway, we break from it.
break;
}
m_idleSEToSceneConnector = null;
}
private void SyncStartLB(Object[] args)
{
string authSceneName = m_localToAuthSceneMapping[m_validLocalScene];
ScriptEngineToSceneConnector sceneConnector = m_SEToSceneConnectors[authSceneName];
sceneConnector.SendLoadBalanceRequest();
}
private void SetQuarkList(Object[] args)
{
m_subscriptionSpaceString = (string)args[0];
InitScriptEngineToSceneConnector(m_subscriptionSpaceString);
}
private void SetQuarkSize(Object[] args)
{
QuarkInfo.SizeX = (int)args[0];
QuarkInfo.SizeY = (int)args[1];
}
private void InitScriptEngineToSceneConnector(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
ScriptEngineToSceneConnector scriptEngineToSceneConnector = new ScriptEngineToSceneConnector(m_scene, authSceneInfo.Addr, authSceneInfo.Port,
m_debugWithViewer, authSceneName, space, m_syncConfig);
if (scriptEngineToSceneConnector.Start())
{
m_SEToSceneConnectors.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_SEToSceneConnectors.Count==0 && m_idleSEToSceneConnector==null)
{
m_log.WarnFormat("[REGION SYNC SCRIPT ENGINE MODULE] Already stopped");
return;
}
if (m_SEToSceneConnectors.Count > 0)
{
foreach (KeyValuePair<string, ScriptEngineToSceneConnector> valPair in m_SEToSceneConnectors)
{
ScriptEngineToSceneConnector connector = valPair.Value;
if (connector == null)
{
continue;
}
connector.Stop();
}
m_SEToSceneConnectors.Clear();
}
else if (m_idleSEToSceneConnector != null)
{
m_idleSEToSceneConnector.Stop();
m_idleSEToSceneConnector = null;
}
//m_scriptEngineToSceneConnector.Stop();
//m_scriptEngineToSceneConnector = null;
m_log.Warn(LogHeader+": Stopping synchronization");
}
m_authScenesInfoByLoc.Clear();
//save script state and stop script instances
m_scene.EventManager.TriggerScriptEngineSyncStop();
//remove all objects
m_scene.DeleteAllSceneObjects();
}
private void SyncStatus(Object[] args)
{
lock (m_client_lock)
{
if (m_SEToSceneConnectors.Count == 0)
{
m_log.WarnFormat("[REGION SYNC SCRIPT ENGINE MODULE] Not currently synchronized");
return;
}
m_log.WarnFormat("[REGION SYNC SCRIPT ENGINE MODULE] Synchronized");
foreach (KeyValuePair<string, ScriptEngineToSceneConnector> pair in m_SEToSceneConnectors)
{
ScriptEngineToSceneConnector sceneConnector = pair.Value;
sceneConnector.ReportStatus();
}
}
}
#endregion
}
}

View File

@ -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 IScriptEngineToSceneConnectorModule
{
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);
}
}

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
@ -10,8 +10,29 @@
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>OpenSim._32BitLaunch</RootNamespace>
<AssemblyName>OpenSim.32BitLaunch</AssemblyName>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<TargetFrameworkProfile />
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -47,6 +68,31 @@
<Name>OpenSim</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.0">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Windows.Installer.3.1">
<Visible>False</Visible>
<ProductName>Windows Installer 3.1</ProductName>
<Install>true</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

Binary file not shown.

Binary file not shown.

View File

@ -1,33 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<runtime>
<gcConcurrent enabled="true" />
<gcServer enabled="true" />
</runtime>
<appSettings>
</appSettings>
<log4net>
<appender name="Console" type="OpenSim.Framework.Console.OpenSimAppender, OpenSim.Framework.Console">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date{HH:mm:ss} - %message" />
</layout>
</appender>
<appender name="LogFileAppender" type="log4net.Appender.FileAppender">
<file value="OpenSim.log" />
<appendToFile value="true" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level - %logger %message%newline" />
</layout>
</appender>
<root>
<level value="DEBUG" />
<appender-ref ref="Console" />
<appender-ref ref="LogFileAppender" />
</root>
</log4net>
</configuration>