Merge branch 'master' into httptests
commit
82fc8e1a36
|
@ -414,6 +414,140 @@ namespace OpenSim.Framework
|
|||
return regionCoord << 8;
|
||||
}
|
||||
|
||||
public static bool checkServiceURI(string uristr, out string serviceURI)
|
||||
{
|
||||
serviceURI = string.Empty;
|
||||
try
|
||||
{
|
||||
Uri uri = new Uri(uristr);
|
||||
serviceURI = uri.AbsoluteUri;
|
||||
if(uri.Port == 80)
|
||||
serviceURI = serviceURI.Trim(new char[] { '/', ' ' }) +":80/";
|
||||
else if(uri.Port == 443)
|
||||
serviceURI = serviceURI.Trim(new char[] { '/', ' ' }) +":443/";
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
serviceURI = string.Empty;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool buildHGRegionURI(string inputName, out string serverURI, out string regionName)
|
||||
{
|
||||
serverURI = string.Empty;
|
||||
regionName = string.Empty;
|
||||
|
||||
inputName = inputName.Trim();
|
||||
|
||||
if (!inputName.StartsWith("http") && !inputName.StartsWith("https"))
|
||||
{
|
||||
// Formats: grid.example.com:8002:region name
|
||||
// grid.example.com:region name
|
||||
// grid.example.com:8002
|
||||
// grid.example.com
|
||||
|
||||
string host;
|
||||
uint port = 80;
|
||||
|
||||
string[] parts = inputName.Split(new char[] { ':' });
|
||||
int indx;
|
||||
if(parts.Length == 0)
|
||||
return false;
|
||||
if (parts.Length == 1)
|
||||
{
|
||||
indx = inputName.IndexOf('/');
|
||||
if (indx < 0)
|
||||
serverURI = "http://"+ inputName + "/";
|
||||
else
|
||||
{
|
||||
serverURI = "http://"+ inputName.Substring(0,indx + 1);
|
||||
if(indx + 2 < inputName.Length)
|
||||
regionName = inputName.Substring(indx + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
host = parts[0];
|
||||
|
||||
if (parts.Length >= 2)
|
||||
{
|
||||
indx = parts[1].IndexOf('/');
|
||||
if(indx < 0)
|
||||
{
|
||||
// If it's a number then assume it's a port. Otherwise, it's a region name.
|
||||
if (!UInt32.TryParse(parts[1], out port))
|
||||
{
|
||||
port = 80;
|
||||
regionName = parts[1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string portstr = parts[1].Substring(0, indx);
|
||||
if(indx + 2 < parts[1].Length)
|
||||
regionName = parts[1].Substring(indx + 1);
|
||||
if (!UInt32.TryParse(portstr, out port))
|
||||
port = 80;
|
||||
}
|
||||
}
|
||||
// always take the last one
|
||||
if (parts.Length >= 3)
|
||||
{
|
||||
regionName = parts[2];
|
||||
}
|
||||
|
||||
serverURI = "http://"+ host +":"+ port.ToString() + "/";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Formats: http://grid.example.com region name
|
||||
// http://grid.example.com "region name"
|
||||
// http://grid.example.com
|
||||
|
||||
string[] parts = inputName.Split(new char[] { ' ' });
|
||||
|
||||
if (parts.Length == 0)
|
||||
return false;
|
||||
|
||||
serverURI = parts[0];
|
||||
|
||||
int indx = serverURI.LastIndexOf('/');
|
||||
if(indx > 10)
|
||||
{
|
||||
if(indx + 2 < inputName.Length)
|
||||
regionName = inputName.Substring(indx + 1);
|
||||
serverURI = inputName.Substring(0, indx + 1);
|
||||
}
|
||||
else if (parts.Length >= 2)
|
||||
{
|
||||
regionName = inputName.Substring(serverURI.Length);
|
||||
}
|
||||
}
|
||||
|
||||
// use better code for sanity check
|
||||
Uri uri;
|
||||
try
|
||||
{
|
||||
uri = new Uri(serverURI);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(regionName))
|
||||
regionName = regionName.Trim(new char[] { '"', ' ' });
|
||||
serverURI = uri.AbsoluteUri;
|
||||
if(uri.Port == 80)
|
||||
serverURI = serverURI.Trim(new char[] { '/', ' ' }) +":80/";
|
||||
else if(uri.Port == 443)
|
||||
serverURI = serverURI.Trim(new char[] { '/', ' ' }) +":443/";
|
||||
return true;
|
||||
}
|
||||
|
||||
public static T Clamp<T>(T x, T min, T max)
|
||||
where T : IComparable<T>
|
||||
{
|
||||
|
|
|
@ -994,7 +994,6 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
|
||||
pbs.TextureEntry = textureEntry.GetBytes();
|
||||
|
||||
bool hasmesh = false;
|
||||
if (inner_instance_list.ContainsKey("mesh")) // seems to happen always but ...
|
||||
{
|
||||
int meshindx = inner_instance_list["mesh"].AsInteger();
|
||||
|
@ -1004,10 +1003,34 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
pbs.SculptType = (byte)SculptType.Mesh;
|
||||
pbs.SculptTexture = meshAssets[meshindx]; // actual asset UUID after meshs suport introduction
|
||||
// data will be requested from asset on rez (i hope)
|
||||
hasmesh = true;
|
||||
}
|
||||
}
|
||||
|
||||
// faces number to pbs shape
|
||||
switch(face_list.Count)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
pbs.ProfileCurve = (byte)ProfileCurve.Circle;
|
||||
pbs.PathCurve = (byte)PathCurve.Circle;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
pbs.ProfileCurve = (byte)ProfileCurve.Circle;
|
||||
pbs.PathCurve = (byte)PathCurve.Line;
|
||||
break;
|
||||
case 5:
|
||||
pbs.ProfileCurve = (byte)ProfileCurve.EqualTriangle;
|
||||
pbs.PathCurve = (byte)PathCurve.Line;
|
||||
break;
|
||||
|
||||
default:
|
||||
pbs.ProfileCurve = (byte)ProfileCurve.Square;
|
||||
pbs.PathCurve = (byte)PathCurve.Line;
|
||||
break;
|
||||
}
|
||||
|
||||
Vector3 position = inner_instance_list["position"].AsVector3();
|
||||
Quaternion rotation = inner_instance_list["rotation"].AsQuaternion();
|
||||
|
||||
|
@ -1018,23 +1041,6 @@ namespace OpenSim.Region.ClientStack.Linden
|
|||
// int material = inner_instance_list["material"].AsInteger();
|
||||
byte material = (byte)Material.Wood;
|
||||
|
||||
// no longer used - begin ------------------------
|
||||
// int mesh = inner_instance_list["mesh"].AsInteger();
|
||||
|
||||
// OSDMap permissions = (OSDMap)inner_instance_list["permissions"];
|
||||
// int base_mask = permissions["base_mask"].AsInteger();
|
||||
// int everyone_mask = permissions["everyone_mask"].AsInteger();
|
||||
// UUID creator_id = permissions["creator_id"].AsUUID();
|
||||
// UUID group_id = permissions["group_id"].AsUUID();
|
||||
// int group_mask = permissions["group_mask"].AsInteger();
|
||||
// bool is_owner_group = permissions["is_owner_group"].AsBoolean();
|
||||
// UUID last_owner_id = permissions["last_owner_id"].AsUUID();
|
||||
// int next_owner_mask = permissions["next_owner_mask"].AsInteger();
|
||||
// UUID owner_id = permissions["owner_id"].AsUUID();
|
||||
// int owner_mask = permissions["owner_mask"].AsInteger();
|
||||
// no longer used - end ------------------------
|
||||
|
||||
|
||||
SceneObjectPart prim
|
||||
= new SceneObjectPart(owner_id, pbs, position, Quaternion.Identity, Vector3.Zero);
|
||||
|
||||
|
|
|
@ -69,6 +69,7 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
Dictionary<UUID, int> m_classifiedInterest = new Dictionary<UUID, int>();
|
||||
|
||||
private JsonRpcRequestManager rpc = new JsonRpcRequestManager();
|
||||
private bool m_allowUserProfileWebURLs = true;
|
||||
|
||||
public Scene Scene
|
||||
{
|
||||
|
@ -160,6 +161,8 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
return;
|
||||
}
|
||||
|
||||
m_allowUserProfileWebURLs = profileConfig.GetBoolean("AllowUserProfileWebURLs", m_allowUserProfileWebURLs);
|
||||
|
||||
m_log.Debug("[PROFILES]: Full Profiles Enabled");
|
||||
ReplaceableInterface = null;
|
||||
Enabled = true;
|
||||
|
@ -316,37 +319,46 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
return;
|
||||
|
||||
IClientAPI remoteClient = (IClientAPI)sender;
|
||||
Dictionary<UUID, string> classifieds = new Dictionary<UUID, string>();
|
||||
|
||||
UUID targetID;
|
||||
UUID.TryParse(args[0], out targetID);
|
||||
|
||||
if(!UUID.TryParse(args[0], out targetID) || targetID == UUID.Zero)
|
||||
return;
|
||||
|
||||
ScenePresence p = FindPresence(targetID);
|
||||
if (p != null && p.isNPC)
|
||||
{
|
||||
remoteClient.SendAvatarClassifiedReply(new UUID(args[0]), new Dictionary<UUID, string>());
|
||||
remoteClient.SendAvatarClassifiedReply(targetID, classifieds);
|
||||
return;
|
||||
}
|
||||
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(targetID, out serverURI);
|
||||
UUID creatorId = UUID.Zero;
|
||||
Dictionary<UUID, string> classifieds = new Dictionary<UUID, string>();
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
{
|
||||
remoteClient.SendAvatarClassifiedReply(targetID, classifieds);
|
||||
return;
|
||||
}
|
||||
|
||||
OSDMap parameters= new OSDMap();
|
||||
UUID.TryParse(args[0], out creatorId);
|
||||
parameters.Add("creatorId", OSD.FromUUID(creatorId));
|
||||
|
||||
parameters.Add("creatorId", OSD.FromUUID(targetID));
|
||||
OSD Params = (OSD)parameters;
|
||||
if(!rpc.JsonRpcRequest(ref Params, "avatarclassifiedsrequest", serverURI, UUID.Random().ToString()))
|
||||
{
|
||||
remoteClient.SendAvatarClassifiedReply(new UUID(args[0]), classifieds);
|
||||
remoteClient.SendAvatarClassifiedReply(targetID, classifieds);
|
||||
return;
|
||||
}
|
||||
|
||||
parameters = (OSDMap)Params;
|
||||
|
||||
OSDArray list = (OSDArray)parameters["result"];
|
||||
if(!parameters.ContainsKey("result") || parameters["result"] == null)
|
||||
{
|
||||
remoteClient.SendAvatarClassifiedReply(targetID, classifieds);
|
||||
return;
|
||||
}
|
||||
|
||||
OSDArray list = (OSDArray)parameters["result"];
|
||||
|
||||
foreach(OSD map in list)
|
||||
{
|
||||
|
@ -360,7 +372,7 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
{
|
||||
if (!m_classifiedCache.ContainsKey(cid))
|
||||
{
|
||||
m_classifiedCache.Add(cid,creatorId);
|
||||
m_classifiedCache.Add(cid,targetID);
|
||||
m_classifiedInterest.Add(cid, 0);
|
||||
}
|
||||
|
||||
|
@ -368,7 +380,7 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
}
|
||||
}
|
||||
|
||||
remoteClient.SendAvatarClassifiedReply(new UUID(args[0]), classifieds);
|
||||
remoteClient.SendAvatarClassifiedReply(targetID, classifieds);
|
||||
}
|
||||
|
||||
public void ClassifiedInfoRequest(UUID queryClassifiedID, IClientAPI remoteClient)
|
||||
|
@ -395,6 +407,10 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(target, out serverURI);
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
object Ad = (object)ad;
|
||||
if(!rpc.JsonRpcRequest(ref Ad, "classifieds_info_query", serverURI, UUID.Random().ToString()))
|
||||
|
@ -465,6 +481,10 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OSDMap parameters = new OSDMap {{"creatorId", OSD.FromUUID(creatorId)}};
|
||||
OSD Params = (OSD)parameters;
|
||||
|
@ -531,10 +551,14 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
{
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
return;
|
||||
|
||||
UUID classifiedId;
|
||||
if(!UUID.TryParse(queryClassifiedID.ToString(), out classifiedId))
|
||||
return;
|
||||
|
||||
OSDMap parameters= new OSDMap();
|
||||
UUID.TryParse(queryClassifiedID.ToString(), out classifiedId);
|
||||
parameters.Add("classifiedId", OSD.FromUUID(classifiedId));
|
||||
OSD Params = (OSD)parameters;
|
||||
if(!rpc.JsonRpcRequest(ref Params, "classified_delete", serverURI, UUID.Random().ToString()))
|
||||
|
@ -569,33 +593,41 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
IClientAPI remoteClient = (IClientAPI)sender;
|
||||
|
||||
UUID targetId;
|
||||
UUID.TryParse(args[0], out targetId);
|
||||
if(!UUID.TryParse(args[0], out targetId))
|
||||
return;
|
||||
|
||||
Dictionary<UUID, string> picks = new Dictionary<UUID, string>();
|
||||
|
||||
// Can't handle NPC yet...
|
||||
ScenePresence p = FindPresence(targetId);
|
||||
|
||||
if (p != null && p.isNPC)
|
||||
{
|
||||
remoteClient.SendAvatarPicksReply(new UUID(args[0]), new Dictionary<UUID, string>());
|
||||
remoteClient.SendAvatarPicksReply(targetId, picks);
|
||||
return;
|
||||
}
|
||||
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(targetId, out serverURI);
|
||||
|
||||
Dictionary<UUID, string> picks = new Dictionary<UUID, string>();
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
{
|
||||
remoteClient.SendAvatarPicksReply(targetId, picks);
|
||||
return;
|
||||
}
|
||||
|
||||
OSDMap parameters= new OSDMap();
|
||||
parameters.Add("creatorId", OSD.FromUUID(targetId));
|
||||
OSD Params = (OSD)parameters;
|
||||
if(!rpc.JsonRpcRequest(ref Params, "avatarpicksrequest", serverURI, UUID.Random().ToString()))
|
||||
{
|
||||
remoteClient.SendAvatarPicksReply(new UUID(args[0]), picks);
|
||||
remoteClient.SendAvatarPicksReply(targetId, picks);
|
||||
return;
|
||||
}
|
||||
|
||||
parameters = (OSDMap)Params;
|
||||
|
||||
if(!parameters.ContainsKey("result") || parameters["result"] == null)
|
||||
{
|
||||
remoteClient.SendAvatarPicksReply(targetId, picks);
|
||||
return;
|
||||
}
|
||||
OSDArray list = (OSDArray)parameters["result"];
|
||||
|
||||
foreach(OSD map in list)
|
||||
|
@ -603,12 +635,9 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
OSDMap m = (OSDMap)map;
|
||||
UUID cid = m["pickuuid"].AsUUID();
|
||||
string name = m["name"].AsString();
|
||||
|
||||
m_log.DebugFormat("[PROFILES]: PicksRequest {0}", name);
|
||||
|
||||
picks[cid] = name;
|
||||
}
|
||||
remoteClient.SendAvatarPicksReply(new UUID(args[0]), picks);
|
||||
remoteClient.SendAvatarPicksReply(targetId, picks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -628,21 +657,28 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
if (!(sender is IClientAPI))
|
||||
return;
|
||||
|
||||
UserProfilePick pick = new UserProfilePick ();
|
||||
UUID targetID;
|
||||
UUID.TryParse (args [0], out targetID);
|
||||
if(!UUID.TryParse(args [0], out targetID))
|
||||
return;
|
||||
|
||||
pick.CreatorId = targetID;
|
||||
|
||||
if(!UUID.TryParse (args [1], out pick.PickId))
|
||||
return;
|
||||
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI (targetID, out serverURI);
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
string theirGatekeeperURI;
|
||||
GetUserGatekeeperURI (targetID, out theirGatekeeperURI);
|
||||
GetUserGatekeeperURI(targetID, out theirGatekeeperURI);
|
||||
|
||||
IClientAPI remoteClient = (IClientAPI)sender;
|
||||
|
||||
UserProfilePick pick = new UserProfilePick ();
|
||||
UUID.TryParse (args [0], out pick.CreatorId);
|
||||
UUID.TryParse (args [1], out pick.PickId);
|
||||
|
||||
|
||||
object Pick = (object)pick;
|
||||
if (!rpc.JsonRpcRequest (ref Pick, "pickinforequest", serverURI, UUID.Random ().ToString ())) {
|
||||
remoteClient.SendAgentAlertMessage (
|
||||
|
@ -652,13 +688,9 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
pick = (UserProfilePick)Pick;
|
||||
|
||||
Vector3 globalPos = new Vector3(Vector3.Zero);
|
||||
Vector3.TryParse(pick.GlobalPos, out globalPos);
|
||||
|
||||
// Smoke and mirrors
|
||||
if (pick.Gatekeeper == MyGatekeeper)
|
||||
{
|
||||
Vector3.TryParse(pick.GlobalPos,out globalPos);
|
||||
}
|
||||
else
|
||||
if (!string.IsNullOrWhiteSpace(MyGatekeeper) && pick.Gatekeeper != MyGatekeeper)
|
||||
{
|
||||
// Setup the illusion
|
||||
string region = string.Format("{0} {1}",pick.Gatekeeper,pick.SimName);
|
||||
|
@ -666,21 +698,19 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
|
||||
if(target == null)
|
||||
{
|
||||
// This is a dead or unreachable region
|
||||
// This is a unreachable region
|
||||
}
|
||||
else
|
||||
{
|
||||
// Work our slight of hand
|
||||
int x = target.RegionLocX;
|
||||
int y = target.RegionLocY;
|
||||
// we have a proxy on map
|
||||
// this is a fail on large regions
|
||||
uint gtmp = (uint)globalPos.X >> 8;
|
||||
globalPos.X -= (gtmp << 8);
|
||||
globalPos.X += target.RegionLocX;
|
||||
|
||||
dynamic synthX = globalPos.X - (globalPos.X/Constants.RegionSize) * Constants.RegionSize;
|
||||
synthX += x;
|
||||
globalPos.X = synthX;
|
||||
|
||||
dynamic synthY = globalPos.Y - (globalPos.Y/Constants.RegionSize) * Constants.RegionSize;
|
||||
synthY += y;
|
||||
globalPos.Y = synthY;
|
||||
gtmp = (uint)globalPos.Y >> 8;
|
||||
globalPos.Y -= (gtmp << 8);
|
||||
globalPos.Y += target.RegionLocY;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -730,6 +760,11 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
UserProfilePick pick = new UserProfilePick();
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ScenePresence p = FindPresence(remoteClient.AgentId);
|
||||
|
||||
Vector3 avaPos = p.AbsolutePosition;
|
||||
|
@ -795,6 +830,10 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
{
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OSDMap parameters= new OSDMap();
|
||||
parameters.Add("pickId", OSD.FromUUID(queryPickID));
|
||||
|
@ -828,11 +867,19 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
if (!(sender is IClientAPI))
|
||||
return;
|
||||
|
||||
if(!UUID.TryParse(args[0], out note.TargetId))
|
||||
return;
|
||||
|
||||
IClientAPI remoteClient = (IClientAPI)sender;
|
||||
note.UserId = remoteClient.AgentId;
|
||||
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
|
||||
note.UserId = remoteClient.AgentId;
|
||||
UUID.TryParse(args[0], out note.TargetId);
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
{
|
||||
remoteClient.SendAvatarNotesReply(note.TargetId, note.Notes);
|
||||
return;
|
||||
}
|
||||
|
||||
object Note = (object)note;
|
||||
if(!rpc.JsonRpcRequest(ref Note, "avatarnotesrequest", serverURI, UUID.Random().ToString()))
|
||||
|
@ -841,7 +888,6 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
return;
|
||||
}
|
||||
note = (UserProfileNotes) Note;
|
||||
|
||||
remoteClient.SendAvatarNotesReply(note.TargetId, note.Notes);
|
||||
}
|
||||
|
||||
|
@ -875,6 +921,8 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
return;
|
||||
|
||||
object Note = note;
|
||||
if(!rpc.JsonRpcRequest(ref Note, "avatar_notes_update", serverURI, UUID.Random().ToString()))
|
||||
|
@ -910,6 +958,8 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
|
||||
string serverURI = string.Empty;
|
||||
bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
return;
|
||||
|
||||
object Pref = pref;
|
||||
if(!rpc.JsonRpcRequest(ref Pref, "user_preferences_update", serverURI, UUID.Random().ToString()))
|
||||
|
@ -934,7 +984,8 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
|
||||
string serverURI = string.Empty;
|
||||
bool foreign = GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
|
||||
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
return;
|
||||
|
||||
object Pref = (object)pref;
|
||||
if(!rpc.JsonRpcRequest(ref Pref, "user_preferences_request", serverURI, UUID.Random().ToString()))
|
||||
|
@ -985,6 +1036,8 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
|
||||
if(string.IsNullOrWhiteSpace(serverURI))
|
||||
return;
|
||||
|
||||
object Param = prop;
|
||||
if(!rpc.JsonRpcRequest(ref Param, "avatar_interests_update", serverURI, UUID.Random().ToString()))
|
||||
|
@ -1004,13 +1057,11 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
return;
|
||||
}
|
||||
|
||||
// Can't handle NPC yet...
|
||||
ScenePresence p = FindPresence(avatarID);
|
||||
|
||||
if (p != null && p.isNPC)
|
||||
{
|
||||
remoteClient.SendAvatarProperties(avatarID, ((INPC)(p.ControllingClient)).profileAbout, ((INPC)(p.ControllingClient)).Born,
|
||||
Utils.StringToBytes("Non Player Character (NPC)"), "NPCs have no life", 16,
|
||||
Utils.StringToBytes("Non Player Character (NPC)"), "NPCs have no life", 0x10,
|
||||
UUID.Zero, ((INPC)(p.ControllingClient)).profileImage, "", UUID.Zero);
|
||||
remoteClient.SendAvatarInterestsReply(avatarID, 0, "",
|
||||
0, "Getting into trouble", "Droidspeak");
|
||||
|
@ -1033,19 +1084,15 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
}
|
||||
|
||||
Byte[] membershipType = new Byte[1];
|
||||
string born = String.Empty;
|
||||
string born = string.Empty;
|
||||
uint flags = 0x00;
|
||||
|
||||
if (null != account)
|
||||
{
|
||||
if (account.UserTitle == "")
|
||||
{
|
||||
membershipType[0] = (Byte)((account.UserFlags & 0xf00) >> 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
membershipType = Utils.StringToBytes(account.UserTitle);
|
||||
}
|
||||
|
||||
born = Util.ToDateTime(account.Created).ToString(
|
||||
"M/d/yyyy", CultureInfo.InvariantCulture);
|
||||
|
@ -1056,16 +1103,13 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
if (GetUserAccountData(avatarID, out userInfo) == true)
|
||||
{
|
||||
if ((string)userInfo["user_title"] == "")
|
||||
{
|
||||
membershipType[0] = (Byte)(((Byte)userInfo["user_flags"] & 0xf00) >> 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
membershipType = Utils.StringToBytes((string)userInfo["user_title"]);
|
||||
}
|
||||
|
||||
int val_born = (int)userInfo["user_created"];
|
||||
born = Util.ToDateTime(val_born).ToString(
|
||||
if(val_born != 0)
|
||||
born = Util.ToDateTime(val_born).ToString(
|
||||
"M/d/yyyy", CultureInfo.InvariantCulture);
|
||||
|
||||
// picky, picky
|
||||
|
@ -1075,16 +1119,21 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
}
|
||||
|
||||
UserProfileProperties props = new UserProfileProperties();
|
||||
string result = string.Empty;
|
||||
|
||||
props.UserId = avatarID;
|
||||
|
||||
if (!GetProfileData(ref props, foreign, out result))
|
||||
string result = string.Empty;
|
||||
if(!GetProfileData(ref props, foreign, out result))
|
||||
{
|
||||
// m_log.DebugFormat("Error getting profile for {0}: {1}", avatarID, result);
|
||||
return;
|
||||
props.AboutText ="Profile not avaible at this time. User may still be unknown to this grid";
|
||||
}
|
||||
|
||||
// if on same region force online
|
||||
if(p != null && !p.IsDeleted)
|
||||
flags |= 0x10;
|
||||
|
||||
if(!m_allowUserProfileWebURLs)
|
||||
props.WebUrl ="";
|
||||
|
||||
remoteClient.SendAvatarProperties(props.UserId, props.AboutText, born, membershipType , props.FirstLifeText, flags,
|
||||
props.FirstLifeImageId, props.ImageId, props.WebUrl, props.PartnerId);
|
||||
|
||||
|
@ -1115,6 +1164,9 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
prop.FirstLifeImageId = newProfile.FirstLifeImage;
|
||||
prop.FirstLifeText = newProfile.FirstLifeAboutText;
|
||||
|
||||
if(!m_allowUserProfileWebURLs)
|
||||
prop.WebUrl ="";
|
||||
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(remoteClient.AgentId, out serverURI);
|
||||
|
||||
|
@ -1153,12 +1205,11 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles
|
|||
|
||||
string serverURI = string.Empty;
|
||||
GetUserProfileServerURI(properties.UserId, out serverURI);
|
||||
|
||||
// This is checking a friend on the home grid
|
||||
// Not HG friend
|
||||
if (String.IsNullOrEmpty(serverURI))
|
||||
{
|
||||
message = "No Presence - foreign friend";
|
||||
message = "User profile service unknown at this time";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -131,7 +131,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Land
|
|||
|
||||
uint rx = 0, ry = 0;
|
||||
Util.RegionHandleToWorldLoc(regionHandle, out rx, out ry);
|
||||
|
||||
rx += x;
|
||||
ry += y;
|
||||
foreach (Scene s in m_Scenes)
|
||||
{
|
||||
uint t = s.RegionInfo.WorldLocX;
|
||||
|
@ -147,6 +148,8 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsIn.Land
|
|||
if( ry < t)
|
||||
{
|
||||
// m_log.Debug("[LAND IN CONNECTOR]: Found region to GetLandData from");
|
||||
x = rx - s.RegionInfo.WorldLocX;
|
||||
y = ry - s.RegionInfo.WorldLocY;
|
||||
regionAccess = s.RegionInfo.AccessLevel;
|
||||
return s.GetLandData(x, y);
|
||||
}
|
||||
|
|
|
@ -52,6 +52,7 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
MethodBase.GetCurrentMethod().DeclaringType);
|
||||
|
||||
private bool m_Enabled = false;
|
||||
private string m_ThisGatekeeper = string.Empty;
|
||||
|
||||
private IGridService m_LocalGridService;
|
||||
private IGridService m_RemoteGridService;
|
||||
|
@ -118,13 +119,20 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
m_LocalGridService = new LocalGridServicesConnector(source, m_RegionInfoCache);
|
||||
if (m_LocalGridService == null)
|
||||
{
|
||||
m_log.Error("[REMOTE GRID CONNECTOR]: failed to loar local connector");
|
||||
m_log.Error("[REMOTE GRID CONNECTOR]: failed to load local connector");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(m_RegionInfoCache == null)
|
||||
m_RegionInfoCache = new RegionInfoCache();
|
||||
|
||||
m_ThisGatekeeper = Util.GetConfigVarFromSections<string>(source, "GatekeeperURI",
|
||||
new string[] { "Startup", "Hypergrid", "GridService" }, String.Empty);
|
||||
// Legacy. Remove soon!
|
||||
m_ThisGatekeeper = gridConfig.GetString("Gatekeeper", m_ThisGatekeeper);
|
||||
|
||||
Util.checkServiceURI(m_ThisGatekeeper, out m_ThisGatekeeper);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -227,12 +235,28 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
return rinfo;
|
||||
}
|
||||
|
||||
public GridRegion GetRegionByName(UUID scopeID, string regionName)
|
||||
public GridRegion GetRegionByName(UUID scopeID, string name)
|
||||
{
|
||||
GridRegion rinfo = m_LocalGridService.GetRegionByName(scopeID, regionName);
|
||||
GridRegion rinfo = m_LocalGridService.GetRegionByName(scopeID, name);
|
||||
if (rinfo != null)
|
||||
return rinfo;
|
||||
|
||||
// HG urls should not get here, strip them
|
||||
// side effect is that local regions with same name as HG may also be found
|
||||
// this mb good or bad
|
||||
string regionName = name;
|
||||
if(name.Contains("."))
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(m_ThisGatekeeper))
|
||||
return rinfo; // no HG
|
||||
|
||||
string regionURI = "";
|
||||
if(!Util.buildHGRegionURI(name, out regionURI, out regionName) || string.IsNullOrWhiteSpace(regionName))
|
||||
return rinfo; // invalid
|
||||
if(m_ThisGatekeeper != regionURI)
|
||||
return rinfo; // not local grid
|
||||
}
|
||||
|
||||
rinfo = m_RemoteGridService.GetRegionByName(scopeID, regionName);
|
||||
m_RegionInfoCache.Cache(scopeID, rinfo);
|
||||
return rinfo;
|
||||
|
@ -242,7 +266,24 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid
|
|||
{
|
||||
List<GridRegion> rinfo = m_LocalGridService.GetRegionsByName(scopeID, name, maxNumber);
|
||||
//m_log.DebugFormat("[REMOTE GRID CONNECTOR]: Local GetRegionsByName {0} found {1} regions", name, rinfo.Count);
|
||||
List<GridRegion> grinfo = m_RemoteGridService.GetRegionsByName(scopeID, name, maxNumber);
|
||||
|
||||
// HG urls should not get here, strip them
|
||||
// side effect is that local regions with same name as HG may also be found
|
||||
// this mb good or bad
|
||||
string regionName = name;
|
||||
if(name.Contains("."))
|
||||
{
|
||||
if(string.IsNullOrWhiteSpace(m_ThisGatekeeper))
|
||||
return rinfo; // no HG
|
||||
|
||||
string regionURI = "";
|
||||
if(!Util.buildHGRegionURI(name, out regionURI, out regionName) || string.IsNullOrWhiteSpace(regionName))
|
||||
return rinfo; // invalid
|
||||
if(m_ThisGatekeeper != regionURI)
|
||||
return rinfo; // not local grid
|
||||
}
|
||||
|
||||
List<GridRegion> grinfo = m_RemoteGridService.GetRegionsByName(scopeID, regionName, maxNumber);
|
||||
|
||||
if (grinfo != null)
|
||||
{
|
||||
|
|
|
@ -15010,7 +15010,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
int rejectTypes = 0;
|
||||
int dataFlags = 0;
|
||||
int maxHits = 1;
|
||||
bool detectPhantom = false;
|
||||
bool notdetectPhantom = true;
|
||||
for (int i = 0; i < options.Length; i += 2)
|
||||
{
|
||||
if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_REJECT_TYPES)
|
||||
|
@ -15020,7 +15020,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_MAX_HITS)
|
||||
maxHits = options.GetLSLIntegerItem(i + 1);
|
||||
else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DETECT_PHANTOM)
|
||||
detectPhantom = (options.GetLSLIntegerItem(i + 1) != 0);
|
||||
notdetectPhantom = (options.GetLSLIntegerItem(i + 1) == 0);
|
||||
}
|
||||
if (maxHits > m_maxHitsInCastRay)
|
||||
maxHits = m_maxHitsInCastRay;
|
||||
|
@ -15050,157 +15050,159 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
World.ForEachSOG(
|
||||
delegate(SceneObjectGroup group)
|
||||
{
|
||||
if(group.IsDeleted || group.RootPart == null)
|
||||
return;
|
||||
// Check group filters unless part filters are configured
|
||||
bool isPhysical = (group.RootPart != null && group.RootPart.PhysActor != null && group.RootPart.PhysActor.IsPhysical);
|
||||
bool isPhysical = (group.RootPart.PhysActor != null && group.RootPart.PhysActor.IsPhysical);
|
||||
bool isNonphysical = !isPhysical;
|
||||
bool isPhantom = group.IsPhantom || group.IsVolumeDetect;
|
||||
bool isAttachment = group.IsAttachment;
|
||||
bool doGroup = true;
|
||||
if (isPhysical && rejectPhysical)
|
||||
doGroup = false;
|
||||
return;
|
||||
if (isNonphysical && rejectNonphysical)
|
||||
doGroup = false;
|
||||
if (isPhantom && detectPhantom)
|
||||
doGroup = true;
|
||||
return;
|
||||
if (isPhantom && notdetectPhantom)
|
||||
return;
|
||||
if (m_filterPartsInCastRay)
|
||||
doGroup = true;
|
||||
return;
|
||||
if (isAttachment && !m_doAttachmentsInCastRay)
|
||||
doGroup = false;
|
||||
return;
|
||||
|
||||
// Parse object/group if passed filters
|
||||
if (doGroup)
|
||||
// Iterate over all prims/parts in object/group
|
||||
foreach(SceneObjectPart part in group.Parts)
|
||||
{
|
||||
// Iterate over all prims/parts in object/group
|
||||
foreach(SceneObjectPart part in group.Parts)
|
||||
// Check part filters if configured
|
||||
if (m_filterPartsInCastRay)
|
||||
{
|
||||
// Check part filters if configured
|
||||
if (m_filterPartsInCastRay)
|
||||
// ignore PhysicsShapeType.None as physics engines do
|
||||
// or we will get into trouble in future
|
||||
if(part.PhysicsShapeType == (byte)PhysicsShapeType.None)
|
||||
continue;
|
||||
isPhysical = (part.PhysActor != null && part.PhysActor.IsPhysical);
|
||||
isNonphysical = !isPhysical;
|
||||
isPhantom = ((part.Flags & PrimFlags.Phantom) != 0) ||
|
||||
(part.VolumeDetectActive);
|
||||
|
||||
if (isPhysical && rejectPhysical)
|
||||
continue;
|
||||
if (isNonphysical && rejectNonphysical)
|
||||
continue;
|
||||
if (isPhantom && notdetectPhantom)
|
||||
continue;
|
||||
}
|
||||
|
||||
// Parse prim/part and project ray if passed filters
|
||||
Vector3 scalePart = part.Scale;
|
||||
Vector3 posPart = part.GetWorldPosition();
|
||||
Quaternion rotPart = part.GetWorldRotation();
|
||||
Quaternion rotPartInv = Quaternion.Inverse(rotPart);
|
||||
Vector3 pos1RayProj = ((pos1Ray - posPart) * rotPartInv) / scalePart;
|
||||
Vector3 pos2RayProj = ((pos2Ray - posPart) * rotPartInv) / scalePart;
|
||||
|
||||
// Filter parts by shape bounding boxes
|
||||
Vector3 shapeBoxMax = new Vector3(0.5f, 0.5f, 0.5f);
|
||||
if (!part.Shape.SculptEntry)
|
||||
shapeBoxMax = shapeBoxMax * (new Vector3(m_primSafetyCoeffX, m_primSafetyCoeffY, m_primSafetyCoeffZ));
|
||||
shapeBoxMax = shapeBoxMax + (new Vector3(tol, tol, tol));
|
||||
if (RayIntersectsShapeBox(pos1RayProj, pos2RayProj, shapeBoxMax))
|
||||
{
|
||||
// Prepare data needed to check for ray hits
|
||||
RayTrans rayTrans = new RayTrans();
|
||||
rayTrans.PartId = part.UUID;
|
||||
rayTrans.GroupId = part.ParentGroup.UUID;
|
||||
rayTrans.Link = group.PrimCount > 1 ? part.LinkNum : 0;
|
||||
rayTrans.ScalePart = scalePart;
|
||||
rayTrans.PositionPart = posPart;
|
||||
rayTrans.RotationPart = rotPart;
|
||||
rayTrans.ShapeNeedsEnds = true;
|
||||
rayTrans.Position1Ray = pos1Ray;
|
||||
rayTrans.Position1RayProj = pos1RayProj;
|
||||
rayTrans.VectorRayProj = pos2RayProj - pos1RayProj;
|
||||
|
||||
// Get detail level depending on type
|
||||
int lod = 0;
|
||||
// Mesh detail level
|
||||
if (part.Shape.SculptEntry && part.Shape.SculptType == (byte)SculptType.Mesh)
|
||||
lod = (int)m_meshLodInCastRay;
|
||||
// Sculpt detail level
|
||||
else if (part.Shape.SculptEntry && part.Shape.SculptType == (byte)SculptType.Mesh)
|
||||
lod = (int)m_sculptLodInCastRay;
|
||||
// Shape detail level
|
||||
else if (!part.Shape.SculptEntry)
|
||||
lod = (int)m_primLodInCastRay;
|
||||
|
||||
// Try to get cached mesh if configured
|
||||
ulong meshKey = 0;
|
||||
FacetedMesh mesh = null;
|
||||
if (m_useMeshCacheInCastRay)
|
||||
{
|
||||
isPhysical = (part.PhysActor != null && part.PhysActor.IsPhysical);
|
||||
isNonphysical = !isPhysical;
|
||||
isPhantom = ((part.Flags & PrimFlags.Phantom) != 0) || (part.VolumeDetectActive);
|
||||
bool doPart = true;
|
||||
if (isPhysical && rejectPhysical)
|
||||
doPart = false;
|
||||
if (isNonphysical && rejectNonphysical)
|
||||
doPart = false;
|
||||
if (isPhantom && detectPhantom)
|
||||
doPart = true;
|
||||
if (!doPart)
|
||||
continue;
|
||||
meshKey = part.Shape.GetMeshKey(Vector3.One, (float)(4 << lod));
|
||||
lock (m_cachedMeshes)
|
||||
{
|
||||
m_cachedMeshes.TryGetValue(meshKey, out mesh);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse prim/part and project ray if passed filters
|
||||
Vector3 scalePart = part.Scale;
|
||||
Vector3 posPart = part.GetWorldPosition();
|
||||
Quaternion rotPart = part.GetWorldRotation();
|
||||
Quaternion rotPartInv = Quaternion.Inverse(rotPart);
|
||||
Vector3 pos1RayProj = ((pos1Ray - posPart) * rotPartInv) / scalePart;
|
||||
Vector3 pos2RayProj = ((pos2Ray - posPart) * rotPartInv) / scalePart;
|
||||
|
||||
// Filter parts by shape bounding boxes
|
||||
Vector3 shapeBoxMax = new Vector3(0.5f, 0.5f, 0.5f);
|
||||
if (!part.Shape.SculptEntry)
|
||||
shapeBoxMax = shapeBoxMax * (new Vector3(m_primSafetyCoeffX, m_primSafetyCoeffY, m_primSafetyCoeffZ));
|
||||
shapeBoxMax = shapeBoxMax + (new Vector3(tol, tol, tol));
|
||||
if (RayIntersectsShapeBox(pos1RayProj, pos2RayProj, shapeBoxMax))
|
||||
// Create mesh if no cached mesh
|
||||
if (mesh == null)
|
||||
{
|
||||
// Prepare data needed to check for ray hits
|
||||
RayTrans rayTrans = new RayTrans();
|
||||
rayTrans.PartId = part.UUID;
|
||||
rayTrans.GroupId = part.ParentGroup.UUID;
|
||||
rayTrans.Link = group.PrimCount > 1 ? part.LinkNum : 0;
|
||||
rayTrans.ScalePart = scalePart;
|
||||
rayTrans.PositionPart = posPart;
|
||||
rayTrans.RotationPart = rotPart;
|
||||
rayTrans.ShapeNeedsEnds = true;
|
||||
rayTrans.Position1Ray = pos1Ray;
|
||||
rayTrans.Position1RayProj = pos1RayProj;
|
||||
rayTrans.VectorRayProj = pos2RayProj - pos1RayProj;
|
||||
// Make an OMV prim to be able to mesh part
|
||||
Primitive omvPrim = part.Shape.ToOmvPrimitive(posPart, rotPart);
|
||||
byte[] sculptAsset = null;
|
||||
if (omvPrim.Sculpt != null)
|
||||
sculptAsset = World.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString());
|
||||
|
||||
// Get detail level depending on type
|
||||
int lod = 0;
|
||||
// Mesh detail level
|
||||
if (part.Shape.SculptEntry && part.Shape.SculptType == (byte)SculptType.Mesh)
|
||||
lod = (int)m_meshLodInCastRay;
|
||||
// Sculpt detail level
|
||||
else if (part.Shape.SculptEntry && part.Shape.SculptType == (byte)SculptType.Mesh)
|
||||
lod = (int)m_sculptLodInCastRay;
|
||||
// Shape detail level
|
||||
else if (!part.Shape.SculptEntry)
|
||||
lod = (int)m_primLodInCastRay;
|
||||
|
||||
// Try to get cached mesh if configured
|
||||
ulong meshKey = 0;
|
||||
FacetedMesh mesh = null;
|
||||
if (m_useMeshCacheInCastRay)
|
||||
// When part is mesh, get mesh
|
||||
if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type == SculptType.Mesh && sculptAsset != null)
|
||||
{
|
||||
meshKey = part.Shape.GetMeshKey(Vector3.One, (float)(4 << lod));
|
||||
lock (m_cachedMeshes)
|
||||
{
|
||||
m_cachedMeshes.TryGetValue(meshKey, out mesh);
|
||||
}
|
||||
AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset);
|
||||
FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, m_meshLodInCastRay, out mesh);
|
||||
meshAsset = null;
|
||||
}
|
||||
|
||||
// Create mesh if no cached mesh
|
||||
if (mesh == null)
|
||||
// When part is sculpt, create mesh
|
||||
// Quirk: Generated sculpt mesh is about 2.8% smaller in X and Y than visual sculpt.
|
||||
else if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type != SculptType.Mesh && sculptAsset != null)
|
||||
{
|
||||
// Make an OMV prim to be able to mesh part
|
||||
Primitive omvPrim = part.Shape.ToOmvPrimitive(posPart, rotPart);
|
||||
byte[] sculptAsset = null;
|
||||
if (omvPrim.Sculpt != null)
|
||||
sculptAsset = World.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString());
|
||||
|
||||
// When part is mesh, get mesh
|
||||
if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type == SculptType.Mesh && sculptAsset != null)
|
||||
IJ2KDecoder imgDecoder = World.RequestModuleInterface<IJ2KDecoder>();
|
||||
if (imgDecoder != null)
|
||||
{
|
||||
AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset);
|
||||
FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, m_meshLodInCastRay, out mesh);
|
||||
meshAsset = null;
|
||||
}
|
||||
|
||||
// When part is sculpt, create mesh
|
||||
// Quirk: Generated sculpt mesh is about 2.8% smaller in X and Y than visual sculpt.
|
||||
else if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type != SculptType.Mesh && sculptAsset != null)
|
||||
{
|
||||
IJ2KDecoder imgDecoder = World.RequestModuleInterface<IJ2KDecoder>();
|
||||
if (imgDecoder != null)
|
||||
Image sculpt = imgDecoder.DecodeToImage(sculptAsset);
|
||||
if (sculpt != null)
|
||||
{
|
||||
Image sculpt = imgDecoder.DecodeToImage(sculptAsset);
|
||||
if (sculpt != null)
|
||||
{
|
||||
mesh = primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt, m_sculptLodInCastRay);
|
||||
sculpt.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When part is shape, create mesh
|
||||
else if (omvPrim.Sculpt == null)
|
||||
{
|
||||
if (
|
||||
omvPrim.PrimData.PathBegin == 0.0 && omvPrim.PrimData.PathEnd == 1.0 &&
|
||||
omvPrim.PrimData.PathTaperX == 0.0 && omvPrim.PrimData.PathTaperY == 0.0 &&
|
||||
omvPrim.PrimData.PathSkew == 0.0 &&
|
||||
omvPrim.PrimData.PathTwist - omvPrim.PrimData.PathTwistBegin == 0.0
|
||||
)
|
||||
rayTrans.ShapeNeedsEnds = false;
|
||||
mesh = primMesher.GenerateFacetedMesh(omvPrim, m_primLodInCastRay);
|
||||
}
|
||||
|
||||
// Cache mesh if configured
|
||||
if (m_useMeshCacheInCastRay && mesh != null)
|
||||
{
|
||||
lock(m_cachedMeshes)
|
||||
{
|
||||
if (!m_cachedMeshes.ContainsKey(meshKey))
|
||||
m_cachedMeshes.Add(meshKey, mesh);
|
||||
mesh = primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt, m_sculptLodInCastRay);
|
||||
sculpt.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check mesh for ray hits
|
||||
AddRayInFacetedMesh(mesh, rayTrans, ref rayHits);
|
||||
mesh = null;
|
||||
|
||||
// When part is shape, create mesh
|
||||
else if (omvPrim.Sculpt == null)
|
||||
{
|
||||
if (
|
||||
omvPrim.PrimData.PathBegin == 0.0 && omvPrim.PrimData.PathEnd == 1.0 &&
|
||||
omvPrim.PrimData.PathTaperX == 0.0 && omvPrim.PrimData.PathTaperY == 0.0 &&
|
||||
omvPrim.PrimData.PathSkew == 0.0 &&
|
||||
omvPrim.PrimData.PathTwist - omvPrim.PrimData.PathTwistBegin == 0.0
|
||||
)
|
||||
rayTrans.ShapeNeedsEnds = false;
|
||||
mesh = primMesher.GenerateFacetedMesh(omvPrim, m_primLodInCastRay);
|
||||
}
|
||||
|
||||
// Cache mesh if configured
|
||||
if (m_useMeshCacheInCastRay && mesh != null)
|
||||
{
|
||||
lock(m_cachedMeshes)
|
||||
{
|
||||
if (!m_cachedMeshes.ContainsKey(meshKey))
|
||||
m_cachedMeshes.Add(meshKey, mesh);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check mesh for ray hits
|
||||
AddRayInFacetedMesh(mesh, rayTrans, ref rayHits);
|
||||
mesh = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1874,6 +1874,55 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
|
|||
"dataserver", resobj, new DetectParams[0]));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Similar to llDie but given an object UUID
|
||||
/// </summary>
|
||||
/// <param name="objectUUID"></param>
|
||||
|
||||
public void osDie(LSL_Key objectUUID)
|
||||
{
|
||||
// CheckThreatLevel(ThreatLevel.VeryHigh, "osDie");
|
||||
// if this is restricted to objects rezzed by this host level can be reduced
|
||||
|
||||
CheckThreatLevel(ThreatLevel.Low, "osDie");
|
||||
m_host.AddScriptLPS(1);
|
||||
|
||||
UUID objUUID;
|
||||
if (!UUID.TryParse(objectUUID, out objUUID))
|
||||
{
|
||||
OSSLShoutError("osDie() cannot delete objects with invalid UUIDs");
|
||||
return;
|
||||
}
|
||||
|
||||
// harakiri check
|
||||
if(objUUID == UUID.Zero)
|
||||
{
|
||||
if (!m_host.ParentGroup.IsAttachment)
|
||||
throw new SelfDeleteException();
|
||||
return;
|
||||
}
|
||||
|
||||
SceneObjectGroup sceneOG = World.GetSceneObjectGroup(objUUID);
|
||||
|
||||
if (sceneOG == null || sceneOG.IsDeleted)
|
||||
return;
|
||||
|
||||
if(sceneOG.IsAttachment)
|
||||
return;
|
||||
|
||||
if (sceneOG.OwnerID != m_host.OwnerID)
|
||||
return;
|
||||
|
||||
// harakiri check
|
||||
if(sceneOG.UUID == m_host.ParentGroup.UUID)
|
||||
throw new SelfDeleteException();
|
||||
|
||||
// restrict to objects rezzed by host
|
||||
if(sceneOG.RezzerID == m_host.ParentGroup.UUID)
|
||||
World.DeleteSceneObject(sceneOG, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write a notecard directly to the prim's inventory.
|
||||
/// </summary>
|
||||
|
|
|
@ -316,6 +316,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces
|
|||
/// </summary>
|
||||
void osForceBreakAllLinks();
|
||||
|
||||
/// <summary>
|
||||
/// Similar to llDie but given an object UUID
|
||||
/// </summary>
|
||||
/// <param name="objectUUID"></param>
|
||||
|
||||
void osDie(LSL_Key objectUUID);
|
||||
|
||||
/// <summary>
|
||||
/// Check if the given key is an npc
|
||||
/// </summary>
|
||||
|
|
|
@ -643,6 +643,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
|
|||
public const int OBJECT_REZZER_KEY = 32;
|
||||
public const int OBJECT_GROUP_TAG = 33;
|
||||
public const int OBJECT_TEMP_ATTACHED = 34;
|
||||
public const int OBJECT_ATTACHED_SLOTS_AVAILABLE = 35;
|
||||
|
||||
// Pathfinding types
|
||||
public const int OPT_OTHER = -1;
|
||||
|
|
|
@ -577,6 +577,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
|
|||
m_OSSL_Functions.osForceBreakAllLinks();
|
||||
}
|
||||
|
||||
public void osDie(LSL_Key objectUUID)
|
||||
{
|
||||
m_OSSL_Functions.osDie(objectUUID);
|
||||
}
|
||||
|
||||
public LSL_Integer osIsNpc(LSL_Key npc)
|
||||
{
|
||||
return m_OSSL_Functions.osIsNpc(npc);
|
||||
|
|
|
@ -66,22 +66,31 @@ namespace OpenSim.Services.Connectors
|
|||
public virtual LandData GetLandData(UUID scopeID, ulong regionHandle, uint x, uint y, out byte regionAccess)
|
||||
{
|
||||
LandData landData = null;
|
||||
Hashtable hash = new Hashtable();
|
||||
hash["region_handle"] = regionHandle.ToString();
|
||||
hash["x"] = x.ToString();
|
||||
hash["y"] = y.ToString();
|
||||
|
||||
IList paramList = new ArrayList();
|
||||
paramList.Add(hash);
|
||||
regionAccess = 42; // Default to adult. Better safe...
|
||||
|
||||
try
|
||||
{
|
||||
uint xpos = 0, ypos = 0;
|
||||
Util.RegionHandleToWorldLoc(regionHandle, out xpos, out ypos);
|
||||
|
||||
GridRegion info = m_GridService.GetRegionByPosition(scopeID, (int)xpos, (int)ypos);
|
||||
if (info != null) // just to be sure
|
||||
{
|
||||
string targetHandlestr = info.RegionHandle.ToString();
|
||||
if( ypos == 0 ) //HG proxy?
|
||||
{
|
||||
// this is real region handle on hg proxies hack
|
||||
targetHandlestr = info.RegionSecret;
|
||||
}
|
||||
|
||||
Hashtable hash = new Hashtable();
|
||||
hash["region_handle"] = targetHandlestr;
|
||||
hash["x"] = x.ToString();
|
||||
hash["y"] = y.ToString();
|
||||
paramList.Add(hash);
|
||||
|
||||
XmlRpcRequest request = new XmlRpcRequest("land_data", paramList);
|
||||
XmlRpcResponse response = request.Send(info.ServerURI, 10000);
|
||||
if (response.IsFault)
|
||||
|
|
|
@ -57,9 +57,6 @@ namespace OpenSim.Services.GridService
|
|||
protected bool m_AllowDuplicateNames = false;
|
||||
protected bool m_AllowHypergridMapSearch = false;
|
||||
|
||||
|
||||
protected bool m_SuppressVarregionOverlapCheckOnRegistration = false;
|
||||
|
||||
private static Dictionary<string,object> m_ExtraFeatures = new Dictionary<string, object>();
|
||||
|
||||
public GridService(IConfigSource config)
|
||||
|
@ -86,8 +83,6 @@ namespace OpenSim.Services.GridService
|
|||
m_AllowDuplicateNames = gridConfig.GetBoolean("AllowDuplicateNames", m_AllowDuplicateNames);
|
||||
m_AllowHypergridMapSearch = gridConfig.GetBoolean("AllowHypergridMapSearch", m_AllowHypergridMapSearch);
|
||||
|
||||
m_SuppressVarregionOverlapCheckOnRegistration = gridConfig.GetBoolean("SuppressVarregionOverlapCheckOnRegistration", m_SuppressVarregionOverlapCheckOnRegistration);
|
||||
|
||||
// This service is also used locally by a simulator running in grid mode. This switches prevents
|
||||
// inappropriate console commands from being registered
|
||||
suppressConsoleCommands = gridConfig.GetBoolean("SuppressConsoleCommands", suppressConsoleCommands);
|
||||
|
@ -202,6 +197,9 @@ namespace OpenSim.Services.GridService
|
|||
if (regionInfos.RegionID == UUID.Zero)
|
||||
return "Invalid RegionID - cannot be zero UUID";
|
||||
|
||||
if (regionInfos.RegionLocY <= Constants.MaximumRegionSize)
|
||||
return "Region location reserved for HG links coord Y must be higher than " + (Constants.MaximumRegionSize/256).ToString();
|
||||
|
||||
String reason = "Region overlaps another region";
|
||||
|
||||
List<RegionData> rdatas = m_Database.Get(
|
||||
|
@ -295,7 +293,7 @@ namespace OpenSim.Services.GridService
|
|||
|
||||
// Region reregistering in other coordinates. Delete the old entry
|
||||
m_log.DebugFormat("[GRID SERVICE]: Region {0} ({1}) was previously registered at {2}-{3}. Deleting old entry.",
|
||||
regionInfos.RegionName, regionInfos.RegionID, regionInfos.RegionLocX, regionInfos.RegionLocY);
|
||||
regionInfos.RegionName, regionInfos.RegionID, regionInfos.RegionCoordX, regionInfos.RegionCoordY);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -505,10 +503,16 @@ namespace OpenSim.Services.GridService
|
|||
{
|
||||
string regionURI = "";
|
||||
string regionName = "";
|
||||
if(!m_HypergridLinker.buildHGRegionURI(name, out regionURI, out regionName))
|
||||
if(!Util.buildHGRegionURI(name, out regionURI, out regionName))
|
||||
return null;
|
||||
|
||||
string mapname = regionURI + regionName;
|
||||
string mapname;
|
||||
bool localGrid = m_HypergridLinker.IsLocalGrid(regionURI);
|
||||
if(localGrid)
|
||||
mapname = regionName;
|
||||
else
|
||||
mapname = regionURI + regionName;
|
||||
|
||||
bool haveMatch = false;
|
||||
|
||||
if (rdatas != null && (rdatas.Count > 0))
|
||||
|
@ -554,14 +558,16 @@ namespace OpenSim.Services.GridService
|
|||
if(haveMatch)
|
||||
return rinfos;
|
||||
}
|
||||
|
||||
string HGname = regionURI +" "+ regionName;
|
||||
GridRegion r = m_HypergridLinker.LinkRegion(scopeID, HGname);
|
||||
if (r != null)
|
||||
if(!localGrid && !string.IsNullOrWhiteSpace(regionURI))
|
||||
{
|
||||
if( count == maxNumber)
|
||||
rinfos.RemoveAt(count - 1);
|
||||
rinfos.Add(r);
|
||||
string HGname = regionURI +" "+ regionName; // include space for compatibility
|
||||
GridRegion r = m_HypergridLinker.LinkRegion(scopeID, HGname);
|
||||
if (r != null)
|
||||
{
|
||||
if( count == maxNumber)
|
||||
rinfos.RemoveAt(count - 1);
|
||||
rinfos.Add(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (rdatas != null && (rdatas.Count > 0))
|
||||
|
@ -589,19 +595,27 @@ namespace OpenSim.Services.GridService
|
|||
{
|
||||
string regionURI = "";
|
||||
string regionName = "";
|
||||
if(!m_HypergridLinker.buildHGRegionURI(name, out regionURI, out regionName))
|
||||
if(!Util.buildHGRegionURI(name, out regionURI, out regionName))
|
||||
return null;
|
||||
|
||||
string mapname = regionURI + regionName;
|
||||
string mapname;
|
||||
bool localGrid = m_HypergridLinker.IsLocalGrid(regionURI);
|
||||
if(localGrid)
|
||||
mapname = regionName;
|
||||
else
|
||||
mapname = regionURI + regionName;
|
||||
|
||||
List<RegionData> rdatas = m_Database.Get(Util.EscapeForLike(mapname), scopeID);
|
||||
if ((rdatas != null) && (rdatas.Count > 0))
|
||||
return RegionData2RegionInfo(rdatas[0]); // get the first
|
||||
|
||||
string HGname = regionURI +" "+ regionName;
|
||||
return m_HypergridLinker.LinkRegion(scopeID, HGname);
|
||||
if(!localGrid && !string.IsNullOrWhiteSpace(regionURI))
|
||||
{
|
||||
string HGname = regionURI +" "+ regionName;
|
||||
return m_HypergridLinker.LinkRegion(scopeID, HGname);
|
||||
}
|
||||
}
|
||||
else
|
||||
return null;
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<GridRegion> GetRegionRange(UUID scopeID, int xmin, int xmax, int ymin, int ymax)
|
||||
|
|
|
@ -137,6 +137,12 @@ namespace OpenSim.Services.GridService
|
|||
m_log.WarnFormat("[HYPERGRID LINKER]: Malformed URL in [GridService], variable Gatekeeper = {0}", m_ThisGatekeeper);
|
||||
}
|
||||
|
||||
m_ThisGatekeeper = m_ThisGatekeeperURI.AbsoluteUri;
|
||||
if(m_ThisGatekeeperURI.Port == 80)
|
||||
m_ThisGatekeeper = m_ThisGatekeeper.Trim(new char[] { '/', ' ' }) +":80/";
|
||||
else if(m_ThisGatekeeperURI.Port == 443)
|
||||
m_ThisGatekeeper = m_ThisGatekeeper.Trim(new char[] { '/', ' ' }) +":443/";
|
||||
|
||||
m_GatekeeperConnector = new GatekeeperServiceConnector(m_AssetService);
|
||||
|
||||
m_log.Debug("[HYPERGRID LINKER]: Loaded all services...");
|
||||
|
@ -191,118 +197,9 @@ namespace OpenSim.Services.GridService
|
|||
return TryLinkRegionToCoords(scopeID, mapName, xloc, yloc, UUID.Zero, out reason);
|
||||
}
|
||||
|
||||
public bool buildHGRegionURI(string inputName, out string serverURI, out string regionName)
|
||||
public bool IsLocalGrid(string serverURI)
|
||||
{
|
||||
serverURI = string.Empty;
|
||||
regionName = string.Empty;
|
||||
|
||||
inputName = inputName.Trim();
|
||||
|
||||
if (!inputName.StartsWith("http") && !inputName.StartsWith("https"))
|
||||
{
|
||||
// Formats: grid.example.com:8002:region name
|
||||
// grid.example.com:region name
|
||||
// grid.example.com:8002
|
||||
// grid.example.com
|
||||
|
||||
string host;
|
||||
uint port = 80;
|
||||
|
||||
string[] parts = inputName.Split(new char[] { ':' });
|
||||
int indx;
|
||||
if(parts.Length == 0)
|
||||
return false;
|
||||
if (parts.Length == 1)
|
||||
{
|
||||
indx = inputName.IndexOf('/');
|
||||
if (indx < 0)
|
||||
serverURI = "http://"+ inputName + "/";
|
||||
else
|
||||
{
|
||||
serverURI = "http://"+ inputName.Substring(0,indx + 1);
|
||||
if(indx + 2 < inputName.Length)
|
||||
regionName = inputName.Substring(indx + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
host = parts[0];
|
||||
|
||||
if (parts.Length >= 2)
|
||||
{
|
||||
indx = parts[1].IndexOf('/');
|
||||
if(indx < 0)
|
||||
{
|
||||
// If it's a number then assume it's a port. Otherwise, it's a region name.
|
||||
if (!UInt32.TryParse(parts[1], out port))
|
||||
{
|
||||
port = 80;
|
||||
regionName = parts[1];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string portstr = parts[1].Substring(0, indx);
|
||||
if(indx + 2 < parts[1].Length)
|
||||
regionName = parts[1].Substring(indx + 1);
|
||||
if (!UInt32.TryParse(portstr, out port))
|
||||
port = 80;
|
||||
}
|
||||
}
|
||||
// always take the last one
|
||||
if (parts.Length >= 3)
|
||||
{
|
||||
regionName = parts[2];
|
||||
}
|
||||
|
||||
serverURI = "http://"+ host +":"+ port.ToString() + "/";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Formats: http://grid.example.com region name
|
||||
// http://grid.example.com "region name"
|
||||
// http://grid.example.com
|
||||
|
||||
string[] parts = inputName.Split(new char[] { ' ' });
|
||||
|
||||
if (parts.Length == 0)
|
||||
return false;
|
||||
|
||||
serverURI = parts[0];
|
||||
|
||||
int indx = serverURI.LastIndexOf('/');
|
||||
if(indx > 10)
|
||||
{
|
||||
if(indx + 2 < inputName.Length)
|
||||
regionName = inputName.Substring(indx + 1);
|
||||
serverURI = inputName.Substring(0, indx + 1);
|
||||
}
|
||||
else if (parts.Length >= 2)
|
||||
{
|
||||
regionName = inputName.Substring(serverURI.Length);
|
||||
}
|
||||
}
|
||||
|
||||
// use better code for sanity check
|
||||
Uri uri;
|
||||
try
|
||||
{
|
||||
uri = new Uri(serverURI);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(regionName))
|
||||
regionName = regionName.Trim(new char[] { '"', ' ' });
|
||||
serverURI = uri.AbsoluteUri;
|
||||
if(uri.Port == 80)
|
||||
serverURI = serverURI.Trim(new char[] { '/', ' ' }) +":80/";
|
||||
else if(uri.Port == 443)
|
||||
serverURI = serverURI.Trim(new char[] { '/', ' ' }) +":443/";
|
||||
return true;
|
||||
return serverURI == m_ThisGatekeeper;
|
||||
}
|
||||
|
||||
public GridRegion TryLinkRegionToCoords(UUID scopeID, string mapName, int xloc, int yloc, UUID ownerID, out string reason)
|
||||
|
@ -313,7 +210,7 @@ namespace OpenSim.Services.GridService
|
|||
string serverURI = string.Empty;
|
||||
string regionName = string.Empty;
|
||||
|
||||
if(!buildHGRegionURI(mapName, out serverURI, out regionName))
|
||||
if(!Util.buildHGRegionURI(mapName, out serverURI, out regionName))
|
||||
{
|
||||
reason = "Wrong URI format for link-region";
|
||||
return null;
|
||||
|
|
|
@ -1203,6 +1203,11 @@
|
|||
;; If un-set / "" the module is disabled
|
||||
;; ProfileServiceURL = ${Const|BaseURL}:${Const|PublicPort}
|
||||
|
||||
;# {AllowUserProfileWebURLs} {} {allow user profiles web urls} {true false} true
|
||||
;; set this to false to prevent your users to be sent to unknown
|
||||
;; web sites by other users on their profiles
|
||||
; AllowUserProfileWebURLs = true
|
||||
|
||||
[XBakes]
|
||||
;# {URL} {} {Set URL for Baked texture service} {}
|
||||
;; Sets the URL for the baked texture ROBUST service.
|
||||
|
|
|
@ -553,6 +553,10 @@
|
|||
;;
|
||||
; ProfileURL = http://127.0.0.1:9000
|
||||
|
||||
;; set this to false to prevent your users to be sent to unknown
|
||||
;; web sites by other users on their profiles
|
||||
; AllowUserProfileWebURLs = true
|
||||
|
||||
|
||||
[SMTP]
|
||||
enabled = false
|
||||
|
|
|
@ -139,8 +139,9 @@
|
|||
Allow_osMessageObject = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER
|
||||
Allow_osRegexIsMatch = true
|
||||
Allow_osGetAvatarHomeURI = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER
|
||||
Allow_osNpcSetProfileAbout = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER
|
||||
Allow_osNpcSetProfileImage = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER
|
||||
Allow_osNpcSetProfileAbout = ${XEngine|osslNPC}
|
||||
Allow_osNpcSetProfileImage = ${XEngine|osslNPC}
|
||||
Allow_osDie = ${XEngine|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER
|
||||
|
||||
; ThreatLevel Moderate
|
||||
Allow_osDropAttachment = ${XEngine|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER
|
||||
|
|
Loading…
Reference in New Issue