extent supported number of avatar textures/bakes/wearables, tell viewers about it on lludp RegionHandShake; propagate agenthover; block teleports/crossings based on worn wearables and peer version;
parent
6aa369d858
commit
9d6c996570
|
@ -51,27 +51,28 @@ namespace OpenSim.Framework
|
|||
|
||||
// this is viewer capabilities and weared things dependent
|
||||
// should be only used as initial default value ( V1 viewers )
|
||||
public readonly static int VISUALPARAM_COUNT = 218;
|
||||
public const int VISUALPARAM_COUNT = 218;
|
||||
|
||||
// public readonly static int TEXTURE_COUNT = 21
|
||||
// 21 bad, make it be updated as libovm gets update
|
||||
// also keeping in sync with it
|
||||
public readonly static int TEXTURE_COUNT = Primitive.TextureEntry.MAX_FACES;
|
||||
// regions and viewer compatibility
|
||||
public readonly static int TEXTURE_COUNT = 45;
|
||||
public const int TEXTURE_COUNT_PV7 = 26;
|
||||
public const int BAKES_COUNT_PV7 = 6;
|
||||
public const int MAXWEARABLE_PV7 = 16;
|
||||
public const int MAXWEARABLE_LEGACY = 14;
|
||||
|
||||
public readonly static byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20 };
|
||||
public readonly static byte[] BAKE_INDICES = new byte[] { 8, 9, 10, 11, 19, 20, 40, 41, 42, 43, 44 };
|
||||
|
||||
protected int m_serial = 0;
|
||||
protected byte[] m_visualparams;
|
||||
protected Primitive.TextureEntry m_texture;
|
||||
protected AvatarWearable[] m_wearables;
|
||||
protected Dictionary<int, List<AvatarAttachment>> m_attachments;
|
||||
protected float m_avatarHeight = 0;
|
||||
protected WearableCacheItem[] m_cacheitems;
|
||||
protected Vector3 m_avatarSize = new Vector3(0.45f, 0.6f, 1.9f); // sl Z cloud value
|
||||
protected Vector3 m_avatarBoxSize = new Vector3(0.45f, 0.6f, 1.9f);
|
||||
protected float m_avatarHeight = 0;
|
||||
protected float m_avatarFeetOffset = 0;
|
||||
protected float m_avatarAnimOffset = 0;
|
||||
protected WearableCacheItem[] m_cacheitems;
|
||||
protected bool m_cacheItemsDirty = true;
|
||||
|
||||
public virtual int Serial
|
||||
{
|
||||
|
@ -128,11 +129,7 @@ namespace OpenSim.Framework
|
|||
set { m_cacheitems = value; }
|
||||
}
|
||||
|
||||
public virtual bool WearableCacheItemsDirty
|
||||
{
|
||||
get { return m_cacheItemsDirty; }
|
||||
set { m_cacheItemsDirty = value; }
|
||||
}
|
||||
public virtual float AvatarPreferencesHoverZ { get; set; }
|
||||
|
||||
public AvatarAppearance()
|
||||
{
|
||||
|
@ -204,12 +201,14 @@ namespace OpenSim.Framework
|
|||
SetDefaultParams();
|
||||
// SetHeight();
|
||||
SetSize(new Vector3(0.45f, 0.6f, 1.9f));
|
||||
AvatarPreferencesHoverZ = 0;
|
||||
m_attachments = new Dictionary<int, List<AvatarAttachment>>();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
m_serial = appearance.Serial;
|
||||
AvatarPreferencesHoverZ = appearance.AvatarPreferencesHoverZ;
|
||||
|
||||
if (copyWearables && (appearance.Wearables != null))
|
||||
{
|
||||
|
@ -228,7 +227,7 @@ namespace OpenSim.Framework
|
|||
m_texture = null;
|
||||
if (appearance.Texture != null)
|
||||
{
|
||||
byte[] tbytes = appearance.Texture.GetBytes();
|
||||
byte[] tbytes = appearance.Texture.GetBakesBytes();
|
||||
m_texture = new Primitive.TextureEntry(tbytes,0,tbytes.Length);
|
||||
if (copyBaked && appearance.m_cacheitems != null)
|
||||
m_cacheitems = (WearableCacheItem[])appearance.m_cacheitems.Clone();
|
||||
|
@ -295,6 +294,7 @@ namespace OpenSim.Framework
|
|||
m_serial = 0;
|
||||
|
||||
SetDefaultTexture();
|
||||
AvatarPreferencesHoverZ = 0;
|
||||
|
||||
//for (int i = 0; i < BAKE_INDICES.Length; i++)
|
||||
// {
|
||||
|
@ -330,9 +330,6 @@ namespace OpenSim.Framework
|
|||
protected virtual void SetDefaultTexture()
|
||||
{
|
||||
m_texture = new Primitive.TextureEntry(new UUID(AppearanceManager.DEFAULT_AVATAR_TEXTURE));
|
||||
|
||||
// for (uint i = 0; i < TEXTURE_COUNT; i++)
|
||||
// m_texture.CreateFace(i).TextureID = new UUID(AppearanceManager.DEFAULT_AVATAR_TEXTURE);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -347,31 +344,31 @@ namespace OpenSim.Framework
|
|||
if (textureEntry == null)
|
||||
return false;
|
||||
|
||||
// There are much simpler versions of this copy that could be
|
||||
// made. We determine if any of the textures actually
|
||||
// changed to know if the appearance should be saved later
|
||||
bool changed = false;
|
||||
for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++)
|
||||
{
|
||||
Primitive.TextureEntryFace newface = textureEntry.FaceTextures[i];
|
||||
Primitive.TextureEntryFace oldface = m_texture.FaceTextures[i];
|
||||
Primitive.TextureEntryFace newface;
|
||||
Primitive.TextureEntryFace tmpFace;
|
||||
|
||||
if (newface == null)
|
||||
//make sure textureEntry.DefaultTexture is the unused one(DEFAULT_AVATAR_TEXTURE).
|
||||
Primitive.TextureEntry converted = new Primitive.TextureEntry(AppearanceManager.DEFAULT_AVATAR_TEXTURE);
|
||||
for (uint i = 0; i < TEXTURE_COUNT; ++i)
|
||||
{
|
||||
newface = textureEntry.GetFace(i);
|
||||
if (newface.TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE)
|
||||
{
|
||||
if (oldface == null)
|
||||
continue;
|
||||
tmpFace = converted.GetFace(i);
|
||||
tmpFace.TextureID = newface.TextureID; // we need a full high level copy, assuming all other parameters are the same.
|
||||
if (m_texture.FaceTextures[i] == null || newface.TextureID != m_texture.FaceTextures[i].TextureID)
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (oldface != null && oldface.TextureID == newface.TextureID)
|
||||
{ if (m_texture.FaceTextures[i] == null)
|
||||
continue;
|
||||
if(m_texture.FaceTextures[i].TextureID != AppearanceManager.DEFAULT_AVATAR_TEXTURE)
|
||||
changed = true;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
m_texture = textureEntry;
|
||||
|
||||
if(changed)
|
||||
m_texture = converted;
|
||||
return changed;
|
||||
}
|
||||
|
||||
|
@ -736,42 +733,69 @@ namespace OpenSim.Framework
|
|||
|
||||
data["serial"] = OSD.FromInteger(m_serial);
|
||||
data["height"] = OSD.FromReal(m_avatarHeight);
|
||||
data["aphz"] = OSD.FromReal(AvatarPreferencesHoverZ);
|
||||
|
||||
if (m_texture == null)
|
||||
return data;
|
||||
|
||||
bool sendPV8 = false;
|
||||
if(ctx != null)
|
||||
sendPV8 = ctx.OutboundVersion >= 0.8;
|
||||
|
||||
// Wearables
|
||||
//
|
||||
// This will send as many or as few wearables as we have, unless a count
|
||||
// is given. Used for legacy (pre 0.4) versions.
|
||||
int count = ctx.WearablesCount;
|
||||
if (ctx.WearablesCount == -1)
|
||||
count = m_wearables.Length;
|
||||
OSDArray wears = new OSDArray(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
OSDArray wears;
|
||||
int count;
|
||||
if (ctx == null)
|
||||
count = MAXWEARABLE_LEGACY;
|
||||
else
|
||||
{
|
||||
AvatarWearable dummyWearable = new AvatarWearable();
|
||||
int wbcount = ctx.WearablesCount;
|
||||
if (wbcount == -1)
|
||||
wbcount = m_wearables.Length;
|
||||
|
||||
if (i < m_wearables.Length)
|
||||
wears.Add(m_wearables[i].Pack());
|
||||
else
|
||||
wears.Add(dummyWearable.Pack());
|
||||
count = wbcount;
|
||||
if(count > MAXWEARABLE_PV7)
|
||||
{
|
||||
count = MAXWEARABLE_PV7;
|
||||
if(sendPV8)
|
||||
{
|
||||
wears = new OSDArray(wbcount - MAXWEARABLE_PV7);
|
||||
for (int i = MAXWEARABLE_PV7; i < wbcount; ++i)
|
||||
wears.Add(m_wearables[i].Pack());
|
||||
|
||||
data["wrbls8"] = wears;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wears = new OSDArray(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
wears.Add(m_wearables[i].Pack());
|
||||
data["wearables"] = wears;
|
||||
|
||||
// Avatar Textures
|
||||
OSDArray textures = new OSDArray(AvatarAppearance.TEXTURE_COUNT);
|
||||
for (uint i = 0; i < AvatarAppearance.TEXTURE_COUNT; i++)
|
||||
// Avatar Textures and preferences hover
|
||||
OSDArray textures;
|
||||
if (sendPV8)
|
||||
{
|
||||
if (m_texture.FaceTextures[i] != null)
|
||||
textures.Add(OSD.FromUUID(m_texture.FaceTextures[i].TextureID));
|
||||
else
|
||||
textures.Add(OSD.FromUUID(AppearanceManager.DEFAULT_AVATAR_TEXTURE));
|
||||
byte[] te = m_texture.GetBakesBytes();
|
||||
data["te8"] = OSD.FromBinary(te);
|
||||
}
|
||||
else
|
||||
{
|
||||
textures = new OSDArray(TEXTURE_COUNT_PV7);
|
||||
for (uint i = 0; i < TEXTURE_COUNT_PV7; i++)
|
||||
textures.Add(OSD.FromUUID(m_texture.GetFace(i).TextureID));
|
||||
data["textures"] = textures;
|
||||
}
|
||||
data["textures"] = textures;
|
||||
|
||||
if (m_cacheitems != null)
|
||||
{
|
||||
OSDArray baked = WearableCacheItem.BakedToOSD(m_cacheitems);
|
||||
if (baked != null)
|
||||
OSDArray baked = WearableCacheItem.BakedToOSD(m_cacheitems, 0, BAKES_COUNT_PV7);
|
||||
if (baked != null && baked.Count > 0)
|
||||
data["bakedcache"] = baked;
|
||||
baked = WearableCacheItem.BakedToOSD(m_cacheitems, BAKES_COUNT_PV7, -1);
|
||||
if (baked != null && baked.Count > 0)
|
||||
data["bc8"] = baked;
|
||||
}
|
||||
|
||||
// Visual Parameters
|
||||
|
@ -810,48 +834,88 @@ namespace OpenSim.Framework
|
|||
OSD tmpOSD;
|
||||
if (data.TryGetValue("serial", out tmpOSD))
|
||||
m_serial = tmpOSD.AsInteger();
|
||||
if(data.TryGetValue("aphz", out tmpOSD))
|
||||
AvatarPreferencesHoverZ = (float)tmpOSD.AsReal();
|
||||
if (data.TryGetValue("height", out tmpOSD))
|
||||
// m_avatarHeight = (float)data["height"].AsReal();
|
||||
SetSize(new Vector3(0.45f,0.6f, (float)tmpOSD.AsReal()));
|
||||
SetSize(new Vector3(0.45f, 0.6f, (float)tmpOSD.AsReal()));
|
||||
|
||||
try
|
||||
{
|
||||
// Wearables
|
||||
OSD tmpOSD8;
|
||||
OSDArray wears8 = null;
|
||||
int wears8Count = 0;
|
||||
|
||||
if (data.TryGetValue("wrbls8", out tmpOSD8) && (tmpOSD8 is OSDArray))
|
||||
{
|
||||
wears8 = (OSDArray)tmpOSD;
|
||||
wears8Count = wears8.Count;
|
||||
}
|
||||
|
||||
if (data.TryGetValue("wearables", out tmpOSD) && (tmpOSD is OSDArray))
|
||||
{
|
||||
OSDArray wears = (OSDArray)tmpOSD;
|
||||
m_wearables = new AvatarWearable[wears.Count];
|
||||
m_wearables = new AvatarWearable[wears.Count + wears8Count];
|
||||
|
||||
for (int i = 0; i < wears.Count; i++)
|
||||
for (int i = 0; i < wears.Count; ++i)
|
||||
m_wearables[i] = new AvatarWearable((OSDArray)wears[i]);
|
||||
if (wears8Count > 0)
|
||||
{
|
||||
for (int i = wears.Count; i < wears8Count + wears.Count; ++i)
|
||||
m_wearables[i] = new AvatarWearable((OSDArray)wears[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_log.Warn("[AVATAR APPEARANCE]: failed to unpack wearables");
|
||||
}
|
||||
|
||||
// Avatar Textures
|
||||
if (data.TryGetValue("textures", out tmpOSD) && (tmpOSD is OSDArray))
|
||||
{
|
||||
OSDArray textures = (OSDArray)tmpOSD;
|
||||
for (int i = 0; i < AvatarAppearance.TEXTURE_COUNT && i < textures.Count; i++)
|
||||
for (int i = 0; i < textures.Count && i < TEXTURE_COUNT_PV7; ++i)
|
||||
{
|
||||
UUID textureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
|
||||
tmpOSD = textures[i];
|
||||
if (tmpOSD != null)
|
||||
textureID = tmpOSD.AsUUID();
|
||||
m_texture.CreateFace((uint)i).TextureID = new UUID(textureID);
|
||||
m_texture.CreateFace((uint)i).TextureID = tmpOSD.AsUUID();
|
||||
}
|
||||
}
|
||||
else
|
||||
if (data.TryGetValue("te8", out tmpOSD))
|
||||
{
|
||||
m_log.Warn("[AVATAR APPEARANCE]: failed to unpack textures");
|
||||
byte[] teb = tmpOSD.AsBinary();
|
||||
Primitive.TextureEntry te = new Primitive.TextureEntry(teb, 0, teb.Length);
|
||||
m_texture = te;
|
||||
}
|
||||
|
||||
if (data.TryGetValue("bakedcache", out tmpOSD) && (tmpOSD is OSDArray))
|
||||
{
|
||||
OSDArray bakedOSDArray = (OSDArray)tmpOSD;
|
||||
m_cacheitems = WearableCacheItem.BakedFromOSD(bakedOSDArray);
|
||||
m_cacheitems = WearableCacheItem.GetDefaultCacheItem();
|
||||
|
||||
bakedOSDArray = (OSDArray)tmpOSD;
|
||||
foreach (OSDMap item in bakedOSDArray)
|
||||
{
|
||||
int idx = item["textureindex"].AsInteger();
|
||||
if (idx < 0 || idx >= m_cacheitems.Length)
|
||||
continue;
|
||||
m_cacheitems[idx].CacheId = item["cacheid"].AsUUID();
|
||||
m_cacheitems[idx].TextureID = item["textureid"].AsUUID();
|
||||
m_cacheitems[idx].TextureAsset = null;
|
||||
}
|
||||
|
||||
if (data.TryGetValue("bc8", out tmpOSD) && (tmpOSD is OSDArray))
|
||||
{
|
||||
bakedOSDArray = (OSDArray)tmpOSD;
|
||||
foreach (OSDMap item in bakedOSDArray)
|
||||
{
|
||||
int idx = item["textureindex"].AsInteger();
|
||||
if (idx < 0 || idx >= m_cacheitems.Length)
|
||||
continue;
|
||||
m_cacheitems[idx].CacheId = item["cacheid"].AsUUID();
|
||||
m_cacheitems[idx].TextureID = item["textureid"].AsUUID();
|
||||
m_cacheitems[idx].TextureAsset = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Visual Parameters
|
||||
|
@ -888,6 +952,32 @@ namespace OpenSim.Framework
|
|||
|
||||
#endregion
|
||||
|
||||
public bool CanTeleport(float version)
|
||||
{
|
||||
if (version >= 0.8)
|
||||
return true;
|
||||
if (m_wearables.Length <= MAXWEARABLE_PV7)
|
||||
return true;
|
||||
for(int i = MAXWEARABLE_PV7; i < m_wearables.Length; ++i)
|
||||
{
|
||||
if(m_wearables[i].Count > 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
// also check baked
|
||||
for(int i = BAKES_COUNT_PV7; i < BAKE_INDICES.Length; i++)
|
||||
{
|
||||
int idx = BAKE_INDICES[i];
|
||||
if (m_texture.FaceTextures[idx] == null)
|
||||
continue;
|
||||
UUID tid = m_texture.FaceTextures[idx].TextureID;
|
||||
if(tid == AppearanceManager.DEFAULT_AVATAR_TEXTURE || tid == UUID.Zero)
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#region VPElement
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -67,10 +67,14 @@ namespace OpenSim.Framework
|
|||
|
||||
public static readonly int ALPHA = 13;
|
||||
public static readonly int TATTOO = 14;
|
||||
|
||||
public static readonly int LEGACY_VERSION_MAX_WEARABLES = 15;
|
||||
// public static readonly int PHYSICS = 15;
|
||||
// public static int MAX_WEARABLES = 16;
|
||||
|
||||
public static readonly int PHYSICS = 15;
|
||||
|
||||
public static int MAX_WEARABLES_PV7 = 16;
|
||||
|
||||
public static readonly int UNIVERSAL = 16;
|
||||
public static int MAX_WEARABLES = 17;
|
||||
|
||||
|
||||
public static readonly UUID DEFAULT_BODY_ITEM = new UUID("66c41e39-38f9-f75a-024e-585989bfaba9");
|
||||
|
|
|
@ -1107,7 +1107,7 @@ namespace OpenSim.Framework
|
|||
/// <param name="agentID">The id of the agent associated with the appearance</param>
|
||||
/// <param name="visualParams"></param>
|
||||
/// <param name="textureEntry"></param>
|
||||
void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry);
|
||||
void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry, float hoverheight);
|
||||
|
||||
void SendCachedTextureResponse(ISceneEntity avatar, int serial, List<CachedTextureResponseArg> cachedTextures);
|
||||
|
||||
|
|
|
@ -220,12 +220,6 @@ namespace OpenSim.Framework.Tests
|
|||
|
||||
AvAppearance.VisualParams = VisualParams;
|
||||
|
||||
List<byte> wearbyte = new List<byte>();
|
||||
for (int i = 0; i < VisualParams.Length; i++)
|
||||
{
|
||||
wearbyte.Add(VisualParams[i]);
|
||||
}
|
||||
|
||||
AvAppearance.SetAppearance(AvAppearance.Texture, (byte[])VisualParams.Clone());
|
||||
}
|
||||
|
||||
|
|
|
@ -85,8 +85,8 @@ namespace OpenSim
|
|||
/// - this is an older teleport protocol used in OpenSimulator 0.7.5 and before.
|
||||
/// </remarks>
|
||||
public readonly static float SimulationServiceVersionAcceptedMin = 0.3f;
|
||||
public readonly static float SimulationServiceVersionAcceptedMax = 0.7f;
|
||||
public readonly static float SimulationServiceVersionAcceptedMax = 0.8f;
|
||||
public readonly static float SimulationServiceVersionSupportedMin = 0.3f;
|
||||
public readonly static float SimulationServiceVersionSupportedMax = 0.7f;
|
||||
public readonly static float SimulationServiceVersionSupportedMax = 0.8f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,31 +128,27 @@ namespace OpenSim.Framework
|
|||
return arr;
|
||||
}
|
||||
|
||||
public static OSDArray BakedToOSD(WearableCacheItem[] pcacheItems)
|
||||
public static OSDArray BakedToOSD(WearableCacheItem[] pcacheItems, int start, int end)
|
||||
{
|
||||
if (pcacheItems.Length < AvatarAppearance.BAKE_INDICES[AvatarAppearance.BAKE_INDICES.Length - 1])
|
||||
OSDArray arr = new OSDArray();
|
||||
if(start < 0)
|
||||
start = 0;
|
||||
if (end < 0 || end > AvatarAppearance.BAKE_INDICES.Length)
|
||||
end = AvatarAppearance.BAKE_INDICES.Length;
|
||||
if (start > end)
|
||||
return null;
|
||||
|
||||
OSDArray arr = new OSDArray();
|
||||
|
||||
for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
|
||||
for (int i = start; i < end; i++)
|
||||
{
|
||||
int idx = AvatarAppearance.BAKE_INDICES[i];
|
||||
|
||||
if(idx >= pcacheItems.Length)
|
||||
continue;
|
||||
WearableCacheItem item = pcacheItems[idx];
|
||||
|
||||
OSDMap itemmap = new OSDMap();
|
||||
itemmap.Add("textureindex", OSD.FromUInteger(item.TextureIndex));
|
||||
itemmap.Add("cacheid", OSD.FromUUID(item.CacheId));
|
||||
itemmap.Add("textureid", OSD.FromUUID(item.TextureID));
|
||||
/*
|
||||
if (item.TextureAsset != null)
|
||||
{
|
||||
itemmap.Add("assetdata", OSD.FromBinary(item.TextureAsset.Data));
|
||||
itemmap.Add("assetcreator", OSD.FromString(item.TextureAsset.CreatorID));
|
||||
itemmap.Add("assetname", OSD.FromString(item.TextureAsset.Name));
|
||||
}
|
||||
*/
|
||||
arr.Add(itemmap);
|
||||
}
|
||||
return arr;
|
||||
|
@ -167,22 +163,11 @@ namespace OpenSim.Framework
|
|||
foreach (OSDMap item in itemarray)
|
||||
{
|
||||
int idx = item["textureindex"].AsInteger();
|
||||
if (idx < 0 || idx > pcache.Length)
|
||||
if (idx < 0 || idx >= pcache.Length)
|
||||
continue;
|
||||
pcache[idx].CacheId = item["cacheid"].AsUUID();
|
||||
pcache[idx].TextureID = item["textureid"].AsUUID();
|
||||
/*
|
||||
if (item.ContainsKey("assetdata"))
|
||||
{
|
||||
AssetBase asset = new AssetBase(item["textureid"].AsUUID(), "BakedTexture", (sbyte)AssetType.Texture, UUID.Zero.ToString());
|
||||
asset.Temporary = true;
|
||||
asset.Local = true;
|
||||
asset.Data = item["assetdata"].AsBinary();
|
||||
pcache[idx].TextureAsset = asset;
|
||||
}
|
||||
else
|
||||
*/
|
||||
pcache[idx].TextureAsset = null;
|
||||
pcache[idx].TextureAsset = null;
|
||||
}
|
||||
}
|
||||
return pcache;
|
||||
|
|
|
@ -989,11 +989,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
//RegionInfo4 block
|
||||
|
||||
//RegionFlagsExtended
|
||||
zc.AddZeros(1); // we dont have this
|
||||
//zc.AddByte(1);
|
||||
//zc.AddUInt64(regionFlags); // we have nothing other base flags
|
||||
//RegionProtocols
|
||||
//zc.AddUInt64(0); // bit 0 signals server side texture baking"
|
||||
//zc.AddZeros(1); // if we dont have this else
|
||||
zc.AddByte(1);
|
||||
zc.AddUInt64(regionFlags); // we have nothing other base flags
|
||||
//RegionProtocols
|
||||
// bit 0 signals server side texture baking
|
||||
// bit 63 signals more than 6 baked textures support"
|
||||
zc.AddUInt64(1UL << 63);
|
||||
|
||||
buf.DataLength = zc.Finish();
|
||||
m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Unknown);
|
||||
|
@ -4434,7 +4436,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
//0xff, 0xff, 0, 1, 158 // ID 158 (low frequency bigendian) zeroencoded
|
||||
};
|
||||
|
||||
public void SendAppearance(UUID targetID, byte[] visualParams, byte[] textureEntry)
|
||||
public void SendAppearance(UUID targetID, byte[] visualParams, byte[] textureEntry, float hover)
|
||||
{
|
||||
// doing post zero encode, because odds of beeing bad are not that low
|
||||
UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint);
|
||||
|
@ -4469,7 +4471,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
|||
// no AppearanceData
|
||||
data[pos++] = 0;
|
||||
// no AppearanceHover
|
||||
data[pos++] = 0;
|
||||
data[pos++] = 1;
|
||||
Utils.FloatToBytesSafepos(0, data, pos); pos += 4;
|
||||
Utils.FloatToBytesSafepos(0, data, pos); pos += 4;
|
||||
Utils.FloatToBytesSafepos(hover, data, pos); pos += 4;
|
||||
|
||||
buf.DataLength = pos;
|
||||
m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority, null, false, true);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Text;
|
||||
|
@ -35,6 +36,7 @@ using log4net;
|
|||
using Nini.Config;
|
||||
using OpenMetaverse;
|
||||
using OpenSim.Framework;
|
||||
using OpenSim.Framework.Monitoring;
|
||||
using OpenSim.Region.Framework.Interfaces;
|
||||
using OpenSim.Region.Framework.Scenes;
|
||||
using OpenSim.Services.Interfaces;
|
||||
|
@ -59,8 +61,10 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
|
||||
private int m_checkTime = 500; // milliseconds to wait between checks for appearance updates
|
||||
private System.Timers.Timer m_updateTimer = new System.Timers.Timer();
|
||||
private Dictionary<UUID,long> m_savequeue = new Dictionary<UUID,long>();
|
||||
private Dictionary<UUID,long> m_sendqueue = new Dictionary<UUID,long>();
|
||||
private ConcurrentDictionary<UUID,long> m_savequeue = new ConcurrentDictionary<UUID,long>();
|
||||
private ConcurrentDictionary<UUID,long> m_sendqueue = new ConcurrentDictionary<UUID,long>();
|
||||
private object m_updatesLock = new object();
|
||||
private int m_updatesbusy = 0;
|
||||
|
||||
private object m_setAppearanceLock = new object();
|
||||
|
||||
|
@ -134,7 +138,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
client.OnRequestWearables += Client_OnRequestWearables;
|
||||
client.OnSetAppearance += Client_OnSetAppearance;
|
||||
client.OnAvatarNowWearing += Client_OnAvatarNowWearing;
|
||||
client.OnCachedTextureRequest += Client_OnCachedTextureRequest;
|
||||
//client.OnCachedTextureRequest += Client_OnCachedTextureRequest;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -232,20 +236,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
sp.SendAppearanceToAllOtherAgents();
|
||||
|
||||
// Send animations back to the avatar as well
|
||||
sp.Animator.SendAnimPack();
|
||||
if(sp.Animator != null)
|
||||
sp.Animator.SendAnimPack();
|
||||
}
|
||||
|
||||
public bool SendAppearance(UUID agentId)
|
||||
{
|
||||
// m_log.DebugFormat("[AVFACTORY]: Sending appearance for {0}", agentId);
|
||||
|
||||
ScenePresence sp = m_scene.GetScenePresence(agentId);
|
||||
if (sp == null)
|
||||
{
|
||||
// This is expected if the user has gone away.
|
||||
// m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentId);
|
||||
if (sp == null || sp.IsDeleted)
|
||||
return false;
|
||||
}
|
||||
|
||||
SendAppearance(sp);
|
||||
return true;
|
||||
|
@ -338,11 +337,8 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
|
||||
// 10000 ticks per millisecond, 1000 milliseconds per second
|
||||
long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_sendtime * 1000 * 10000);
|
||||
lock (m_sendqueue)
|
||||
{
|
||||
m_sendqueue[agentid] = timestamp;
|
||||
m_updateTimer.Start();
|
||||
}
|
||||
m_sendqueue[agentid] = timestamp;
|
||||
m_updateTimer.Start();
|
||||
}
|
||||
|
||||
public void QueueAppearanceSave(UUID agentid)
|
||||
|
@ -351,11 +347,8 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
|
||||
// 10000 ticks per millisecond, 1000 milliseconds per second
|
||||
long timestamp = DateTime.Now.Ticks + Convert.ToInt64(m_savetime * 1000 * 10000);
|
||||
lock (m_savequeue)
|
||||
{
|
||||
m_savequeue[agentid] = timestamp;
|
||||
m_updateTimer.Start();
|
||||
}
|
||||
m_savequeue[agentid] = timestamp;
|
||||
m_updateTimer.Start();
|
||||
}
|
||||
|
||||
// called on textures update
|
||||
|
@ -370,106 +363,90 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
|
||||
// uploaded baked textures will be in assets local cache
|
||||
IAssetCache cache = m_scene.RequestModuleInterface<IAssetCache>();
|
||||
IBakedTextureModule m_BakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
|
||||
|
||||
int validDirtyBakes = 0;
|
||||
int hits = 0;
|
||||
|
||||
// our main cacheIDs mapper is p.Appearance.WearableCacheItems
|
||||
WearableCacheItem[] wearableCache = sp.Appearance.WearableCacheItems;
|
||||
bool hadSkirt = false;
|
||||
|
||||
WearableCacheItem[] wearableCache = sp.Appearance.WearableCacheItems;
|
||||
if (wearableCache == null)
|
||||
{
|
||||
wearableCache = WearableCacheItem.GetDefaultCacheItem();
|
||||
else
|
||||
{
|
||||
hadSkirt = (wearableCache[19].TextureID != UUID.Zero);
|
||||
}
|
||||
|
||||
HashSet<uint> updatedFaces = new HashSet<uint>();
|
||||
List<UUID> missing = new List<UUID>();
|
||||
|
||||
bool haveSkirt = (wearableCache[19].TextureID != UUID.Zero);
|
||||
bool haveNewSkirt = false;
|
||||
|
||||
// Process received baked textures
|
||||
for (int i = 0; i < cacheItems.Length; i++)
|
||||
{
|
||||
int idx = (int)cacheItems[i].TextureIndex;
|
||||
uint idx = cacheItems[i].TextureIndex;
|
||||
if(idx >= AvatarAppearance.TEXTURE_COUNT)
|
||||
{
|
||||
hits++;
|
||||
continue;
|
||||
}
|
||||
|
||||
updatedFaces.Add(idx);
|
||||
|
||||
wearableCache[idx].TextureAsset = null; // just in case
|
||||
Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
|
||||
|
||||
// No face
|
||||
if (face == null)
|
||||
if (face == null || face.TextureID == UUID.Zero || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
|
||||
{
|
||||
// for some reason viewer is cleaning this
|
||||
if(idx != 19) // skirt is optional
|
||||
{
|
||||
sp.Appearance.Texture.FaceTextures[idx] = sp.Appearance.Texture.CreateFace((uint) idx);
|
||||
sp.Appearance.Texture.FaceTextures[idx].TextureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
|
||||
}
|
||||
wearableCache[idx].CacheId = UUID.Zero;
|
||||
wearableCache[idx].TextureID = UUID.Zero;
|
||||
wearableCache[idx].TextureAsset = null;
|
||||
if (idx == 19)
|
||||
{
|
||||
hits++;
|
||||
if(hadSkirt)
|
||||
validDirtyBakes++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cache != null)
|
||||
{
|
||||
AssetBase asb = null;
|
||||
cache.Get(face.TextureID.ToString(), out asb);
|
||||
wearableCache[idx].TextureAsset = asb;
|
||||
}
|
||||
|
||||
if (wearableCache[idx].TextureAsset != null)
|
||||
{
|
||||
if ( wearableCache[idx].TextureID != face.TextureID ||
|
||||
wearableCache[idx].CacheId != cacheItems[i].CacheId)
|
||||
validDirtyBakes++;
|
||||
|
||||
wearableCache[idx].TextureID = face.TextureID;
|
||||
wearableCache[idx].CacheId = cacheItems[i].CacheId;
|
||||
hits++;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (face.TextureID == UUID.Zero || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
|
||||
{
|
||||
wearableCache[idx].CacheId = UUID.Zero;
|
||||
wearableCache[idx].TextureID = UUID.Zero;
|
||||
wearableCache[idx].TextureAsset = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(idx == 19)
|
||||
haveNewSkirt = true;
|
||||
/*
|
||||
if (face.TextureID == wearableCache[idx].TextureID && m_BakedTextureModule != null)
|
||||
{
|
||||
if (wearableCache[idx].CacheId != cacheItems[i].CacheId)
|
||||
{
|
||||
wearableCache[idx].CacheId = cacheItems[i].CacheId;
|
||||
validDirtyBakes++;
|
||||
|
||||
//assuming this can only happen if asset is in cache
|
||||
}
|
||||
hits++;
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
wearableCache[idx].TextureAsset = null;
|
||||
if (cache != null)
|
||||
{
|
||||
AssetBase asb = null;
|
||||
cache.Get(face.TextureID.ToString(), out asb);
|
||||
wearableCache[idx].TextureAsset = asb;
|
||||
}
|
||||
|
||||
if (wearableCache[idx].TextureAsset != null)
|
||||
{
|
||||
if ( wearableCache[idx].TextureID != face.TextureID ||
|
||||
wearableCache[idx].CacheId != cacheItems[i].CacheId)
|
||||
validDirtyBakes++;
|
||||
|
||||
wearableCache[idx].TextureID = face.TextureID;
|
||||
wearableCache[idx].CacheId = cacheItems[i].CacheId;
|
||||
hits++;
|
||||
}
|
||||
else
|
||||
{
|
||||
wearableCache[idx].CacheId = UUID.Zero;
|
||||
wearableCache[idx].TextureID = UUID.Zero;
|
||||
wearableCache[idx].TextureAsset = null;
|
||||
missing.Add(face.TextureID);
|
||||
continue;
|
||||
}
|
||||
wearableCache[idx].CacheId = UUID.Zero;
|
||||
wearableCache[idx].TextureID = UUID.Zero;
|
||||
missing.Add(face.TextureID);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// handle optional skirt case
|
||||
if(!haveNewSkirt && haveSkirt)
|
||||
// this may be a current fs bug
|
||||
for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
|
||||
{
|
||||
wearableCache[19].CacheId = UUID.Zero;
|
||||
wearableCache[19].TextureID = UUID.Zero;
|
||||
wearableCache[19].TextureAsset = null;
|
||||
validDirtyBakes++;
|
||||
uint idx = AvatarAppearance.BAKE_INDICES[i];
|
||||
if(updatedFaces.Contains(idx))
|
||||
continue;
|
||||
|
||||
sp.Appearance.Texture.FaceTextures[idx] = null;
|
||||
|
||||
wearableCache[idx].CacheId = UUID.Zero;
|
||||
wearableCache[idx].TextureID = UUID.Zero;
|
||||
wearableCache[idx].TextureAsset = null;
|
||||
}
|
||||
|
||||
sp.Appearance.WearableCacheItems = wearableCache;
|
||||
|
@ -480,15 +457,18 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
sp.ControllingClient.SendRebakeAvatarTextures(id);
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
if (validDirtyBakes > 0 && hits == cacheItems.Length)
|
||||
{
|
||||
// if we got a full set of baked textures save all in BakedTextureModule
|
||||
IBakedTextureModule m_BakedTextureModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
|
||||
if (m_BakedTextureModule != null)
|
||||
{
|
||||
m_log.DebugFormat("[UpdateBakedCache] Uploading to Bakes Server: cache hits: {0} changed entries: {1} rebakes {2}",
|
||||
hits.ToString(), validDirtyBakes.ToString(), missing.Count);
|
||||
|
||||
m_BakedTextureModule.Store(sp.UUID, wearableCache);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -505,26 +485,25 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
// sp.Appearance.WearableCacheItems[j].TextureID);
|
||||
}
|
||||
|
||||
return (hits == cacheItems.Length);
|
||||
return changed;
|
||||
}
|
||||
|
||||
// called when we get a new root avatar
|
||||
public bool ValidateBakedTextureCache(IScenePresence sp)
|
||||
{
|
||||
int hits = 0;
|
||||
|
||||
if (((ScenePresence)sp).IsNPC)
|
||||
return true;
|
||||
|
||||
int hits = 0;
|
||||
|
||||
IAssetCache cache = m_scene.RequestModuleInterface<IAssetCache>();
|
||||
if (cache == null)
|
||||
return false;
|
||||
|
||||
IBakedTextureModule bakedModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
|
||||
|
||||
lock (m_setAppearanceLock)
|
||||
{
|
||||
IAssetCache cache = m_scene.RequestModuleInterface<IAssetCache>();
|
||||
IBakedTextureModule bakedModule = m_scene.RequestModuleInterface<IBakedTextureModule>();
|
||||
WearableCacheItem[] bakedModuleCache = null;
|
||||
|
||||
if (cache == null)
|
||||
return false;
|
||||
|
||||
WearableCacheItem[] wearableCache = sp.Appearance.WearableCacheItems;
|
||||
|
||||
// big debug
|
||||
|
@ -566,70 +545,47 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
}
|
||||
else
|
||||
{
|
||||
// we may have received a full cache
|
||||
// check same coerence and store
|
||||
wearableCacheValid = true;
|
||||
Primitive.TextureEntryFace face;
|
||||
for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
|
||||
{
|
||||
int idx = AvatarAppearance.BAKE_INDICES[i];
|
||||
Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
|
||||
if (face != null)
|
||||
face = sp.Appearance.Texture.FaceTextures[idx];
|
||||
|
||||
if(face == null || face.TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
|
||||
{
|
||||
if (face.TextureID == wearableCache[idx].TextureID &&
|
||||
face.TextureID != UUID.Zero)
|
||||
{
|
||||
if (wearableCache[idx].TextureAsset != null)
|
||||
{
|
||||
hits++;
|
||||
wearableCache[idx].TextureAsset.Temporary = true;
|
||||
wearableCache[idx].TextureAsset.Local = true;
|
||||
cache.Cache(wearableCache[idx].TextureAsset);
|
||||
wearableCache[idx].TextureAsset = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cache.Check((wearableCache[idx].TextureID).ToString()))
|
||||
{
|
||||
hits++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
wearableCacheValid = false;
|
||||
wearableCache[idx].CacheId = UUID.Zero;
|
||||
wearableCache[idx].TextureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
|
||||
hits++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
wearableCacheValid = (wearableCacheValid && (hits >= AvatarAppearance.BAKE_INDICES.Length - 1));
|
||||
if (wearableCacheValid)
|
||||
{
|
||||
// m_log.Debug("[ValidateBakedCache] have valid local cache");
|
||||
if (face.TextureID == wearableCache[idx].TextureID &&
|
||||
face.TextureID != UUID.Zero)
|
||||
{
|
||||
if (cache.Check((wearableCache[idx].TextureID).ToString()))
|
||||
{
|
||||
hits++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
wearableCache[idx].CacheId = UUID.Zero;
|
||||
wearableCache[idx].TextureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
|
||||
wearableCacheValid = false;
|
||||
}
|
||||
else
|
||||
wearableCache[19].TextureAsset = null; // clear optional skirt
|
||||
}
|
||||
|
||||
bool checkExternal = false;
|
||||
|
||||
if (!wearableCacheValid)
|
||||
{
|
||||
hits = 0;
|
||||
// only use external bake module on login condition check
|
||||
// ScenePresence ssp = null;
|
||||
// if (sp is ScenePresence)
|
||||
{
|
||||
// ssp = (ScenePresence)sp;
|
||||
// checkExternal = (((uint)ssp.TeleportFlags & (uint)TeleportFlags.ViaLogin) != 0) &&
|
||||
// bakedModule != null;
|
||||
|
||||
// or do it anytime we dont have the cache
|
||||
checkExternal = bakedModule != null;
|
||||
}
|
||||
}
|
||||
checkExternal = bakedModule != null;
|
||||
|
||||
if (checkExternal)
|
||||
{
|
||||
WearableCacheItem[] bakedModuleCache = null;
|
||||
bool gotbacked = false;
|
||||
hits = 0;
|
||||
|
||||
// m_log.Debug("[ValidateBakedCache] local cache invalid, checking bakedModule");
|
||||
// m_log.Debug("[ValidateBakedCache] local cache invalid, checking bakedModule");
|
||||
try
|
||||
{
|
||||
bakedModuleCache = bakedModule.Get(sp.UUID);
|
||||
|
@ -647,8 +603,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
for (int i = 0; i < bakedModuleCache.Length; i++)
|
||||
{
|
||||
int j = (int)bakedModuleCache[i].TextureIndex;
|
||||
|
||||
if (bakedModuleCache[i].TextureAsset != null)
|
||||
if (j < AvatarAppearance.TEXTURE_COUNT && bakedModuleCache[i].TextureAsset != null)
|
||||
{
|
||||
wearableCache[j].TextureID = bakedModuleCache[i].TextureID;
|
||||
wearableCache[j].CacheId = bakedModuleCache[i].CacheId;
|
||||
|
@ -658,33 +613,27 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
cache.Cache(bakedModuleCache[i].TextureAsset);
|
||||
}
|
||||
}
|
||||
gotbacked = true;
|
||||
}
|
||||
|
||||
if (gotbacked)
|
||||
{
|
||||
// force the ones we got
|
||||
for (int i = 0; i < AvatarAppearance.BAKE_INDICES.Length; i++)
|
||||
{
|
||||
int idx = AvatarAppearance.BAKE_INDICES[i];
|
||||
if(wearableCache[idx].TextureAsset == null)
|
||||
if (wearableCache[idx].TextureAsset == null)
|
||||
{
|
||||
if(idx == 19)
|
||||
{
|
||||
sp.Appearance.Texture.FaceTextures[idx] = null;
|
||||
hits++;
|
||||
}
|
||||
else if(sp.Appearance.Texture.FaceTextures[idx] == null ||
|
||||
sp.Appearance.Texture.FaceTextures[idx].TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
|
||||
hits++;
|
||||
wearableCache[idx].TextureID = AppearanceManager.DEFAULT_AVATAR_TEXTURE;
|
||||
wearableCache[idx].CacheId = UUID.Zero;
|
||||
continue;
|
||||
}
|
||||
|
||||
Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[idx];
|
||||
|
||||
if (face == null)
|
||||
{
|
||||
face = sp.Appearance.Texture.CreateFace((uint)idx);
|
||||
sp.Appearance.Texture.FaceTextures[idx] = face;
|
||||
}
|
||||
|
||||
Primitive.TextureEntryFace face = sp.Appearance.Texture.GetFace((uint)idx);
|
||||
face.TextureID = wearableCache[idx].TextureID;
|
||||
hits++;
|
||||
wearableCache[idx].TextureAsset = null;
|
||||
|
@ -708,7 +657,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
sp.Appearance.WearableCacheItems[j].TextureID);
|
||||
}
|
||||
*/
|
||||
return (hits >= AvatarAppearance.BAKE_INDICES.Length - 1); // skirt is optional
|
||||
return (hits >= AvatarAppearance.BAKE_INDICES.Length); // skirt is optional
|
||||
}
|
||||
|
||||
public int RequestRebake(IScenePresence sp, bool missingTexturesOnly)
|
||||
|
@ -776,13 +725,15 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
foreach (int i in Enum.GetValues(typeof(BakeType)))
|
||||
{
|
||||
BakeType bakeType = (BakeType)i;
|
||||
if (bakeType == BakeType.NumberOfEntries)
|
||||
break;
|
||||
|
||||
if (bakeType == BakeType.Unknown)
|
||||
continue;
|
||||
|
||||
// m_log.DebugFormat(
|
||||
// "[AVFACTORY]: NPC avatar {0} has texture id {1} : {2}",
|
||||
// acd.AgentID, i, acd.Appearance.Texture.FaceTextures[i]);
|
||||
// m_log.DebugFormat(
|
||||
// "[AVFACTORY]: NPC avatar {0} has texture id {1} : {2}",
|
||||
// acd.AgentID, i, acd.Appearance.Texture.FaceTextures[i]);
|
||||
|
||||
int ftIndex = (int)AppearanceManager.BakeTypeToAgentTextureIndex(bakeType);
|
||||
Primitive.TextureEntryFace texture = faceTextures[ftIndex]; // this will be null if there's no such baked texture
|
||||
|
@ -794,90 +745,78 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
|
||||
private void HandleAppearanceUpdateTimer(object sender, EventArgs ea)
|
||||
{
|
||||
long now = DateTime.Now.Ticks;
|
||||
|
||||
lock (m_sendqueue)
|
||||
if(Monitor.TryEnter(m_updatesLock))
|
||||
{
|
||||
Dictionary<UUID, long> sends = new Dictionary<UUID, long>(m_sendqueue);
|
||||
foreach (KeyValuePair<UUID, long> kvp in sends)
|
||||
UUID id;
|
||||
long now = DateTime.Now.Ticks;
|
||||
|
||||
foreach (KeyValuePair<UUID, long> kvp in m_sendqueue)
|
||||
{
|
||||
// We have to load the key and value into local parameters to avoid a race condition if we loop
|
||||
// around and load kvp with a different value before FireAndForget has launched its thread.
|
||||
UUID avatarID = kvp.Key;
|
||||
long sendTime = kvp.Value;
|
||||
if (sendTime > now)
|
||||
continue;
|
||||
|
||||
// m_log.DebugFormat("[AVFACTORY]: Handling queued appearance updates for {0}, update delta to now is {1}", avatarID, sendTime - now);
|
||||
|
||||
if (sendTime < now)
|
||||
{
|
||||
Util.FireAndForget(o => SendAppearance(avatarID), null, "AvatarFactoryModule.SendAppearance");
|
||||
m_sendqueue.Remove(avatarID);
|
||||
}
|
||||
id = kvp.Key;
|
||||
m_sendqueue.TryRemove(id, out sendTime);
|
||||
SendAppearance(id);
|
||||
}
|
||||
}
|
||||
|
||||
lock (m_savequeue)
|
||||
{
|
||||
Dictionary<UUID, long> saves = new Dictionary<UUID, long>(m_savequeue);
|
||||
foreach (KeyValuePair<UUID, long> kvp in saves)
|
||||
if(m_updatesbusy == 0)
|
||||
{
|
||||
// We have to load the key and value into local parameters to avoid a race condition if we loop
|
||||
// around and load kvp with a different value before FireAndForget has launched its thread.
|
||||
UUID avatarID = kvp.Key;
|
||||
long sendTime = kvp.Value;
|
||||
|
||||
if (sendTime < now)
|
||||
m_updatesbusy = -1;
|
||||
List<UUID> saves = new List<UUID>(m_savequeue.Count);
|
||||
foreach (KeyValuePair<UUID, long> kvp in m_savequeue)
|
||||
{
|
||||
Util.FireAndForget(o => SaveAppearance(avatarID), null, "AvatarFactoryModule.SaveAppearance");
|
||||
m_savequeue.Remove(avatarID);
|
||||
long sendTime = kvp.Value;
|
||||
if (sendTime > now)
|
||||
continue;
|
||||
|
||||
id = kvp.Key;
|
||||
m_savequeue.TryRemove(id, out sendTime);
|
||||
saves.Add(id);
|
||||
}
|
||||
|
||||
m_updatesbusy = 0;
|
||||
if (saves.Count > 0)
|
||||
{
|
||||
++m_updatesbusy;
|
||||
WorkManager.RunInThreadPool(
|
||||
delegate
|
||||
{
|
||||
SaveAppearance(saves);
|
||||
saves = null;
|
||||
--m_updatesbusy;
|
||||
}, null, string.Format("SaveAppearance ({0})", m_scene.Name));
|
||||
}
|
||||
}
|
||||
|
||||
// We must lock both queues here so that QueueAppearanceSave() or *Send() don't m_updateTimer.Start() on
|
||||
// another thread inbetween the first count calls and m_updateTimer.Stop() on this thread.
|
||||
lock (m_sendqueue)
|
||||
if (m_savequeue.Count == 0 && m_sendqueue.Count == 0)
|
||||
m_updateTimer.Stop();
|
||||
if (m_savequeue.Count == 0 && m_sendqueue.Count == 0)
|
||||
m_updateTimer.Stop();
|
||||
|
||||
Monitor.Exit(m_updatesLock);
|
||||
}
|
||||
}
|
||||
|
||||
private void SaveAppearance(UUID agentid)
|
||||
private void SaveAppearance(List<UUID> ids)
|
||||
{
|
||||
// We must set appearance parameters in the en_US culture in order to avoid issues where values are saved
|
||||
// in a culture where decimal points are commas and then reloaded in a culture which just treats them as
|
||||
// number seperators.
|
||||
Culture.SetCurrentCulture();
|
||||
|
||||
ScenePresence sp = m_scene.GetScenePresence(agentid);
|
||||
if (sp == null)
|
||||
{
|
||||
// This is expected if the user has gone away.
|
||||
// m_log.DebugFormat("[AVFACTORY]: Agent {0} no longer in the scene", agentid);
|
||||
return;
|
||||
}
|
||||
|
||||
// m_log.DebugFormat("[AVFACTORY]: Saving appearance for avatar {0}", agentid);
|
||||
|
||||
// This could take awhile since it needs to pull inventory
|
||||
// We need to do it at the point of save so that there is a sufficient delay for any upload of new body part/shape
|
||||
// assets and item asset id changes to complete.
|
||||
// I don't think we need to worry about doing this within m_setAppearanceLock since the queueing avoids
|
||||
// multiple save requests.
|
||||
SetAppearanceAssets(sp.UUID, sp.Appearance);
|
||||
foreach(UUID id in ids)
|
||||
{
|
||||
ScenePresence sp = m_scene.GetScenePresence(id);
|
||||
if(sp == null)
|
||||
continue;
|
||||
// This could take awhile since it needs to pull inventory
|
||||
// We need to do it at the point of save so that there is a sufficient delay for any upload of new body part/shape
|
||||
// assets and item asset id changes to complete.
|
||||
// I don't think we need to worry about doing this within m_setAppearanceLock since the queueing avoids
|
||||
// multiple save requests.
|
||||
|
||||
// List<AvatarAttachment> attachments = sp.Appearance.GetAttachments();
|
||||
// foreach (AvatarAttachment att in attachments)
|
||||
// {
|
||||
// m_log.DebugFormat(
|
||||
// "[AVFACTORY]: For {0} saving attachment {1} at point {2}",
|
||||
// sp.Name, att.ItemID, att.AttachPoint);
|
||||
// }
|
||||
SetAppearanceAssets(id, sp.Appearance);
|
||||
|
||||
m_scene.AvatarService.SetAppearance(agentid, sp.Appearance);
|
||||
|
||||
// Trigger this here because it's the final step in the set/queue/save process for appearance setting.
|
||||
// Everything has been updated and stored. Ensures bakes have been persisted (if option is set to persist bakes).
|
||||
m_scene.EventManager.TriggerAvatarAppearanceChanged(sp);
|
||||
m_scene.AvatarService.SetAppearance(id, sp.Appearance);
|
||||
//m_scene.EventManager.TriggerAvatarAppearanceChanged(sp);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1231,7 +1170,7 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
// m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance called for {0} ({1})", client.Name, client.AgentId);
|
||||
ScenePresence sp = m_scene.GetScenePresence(client.AgentId);
|
||||
if (sp != null)
|
||||
SetAppearance(sp, textureEntry, visualParams,avSize, cacheItems);
|
||||
SetAppearance(sp, textureEntry, visualParams, avSize, cacheItems);
|
||||
else
|
||||
m_log.WarnFormat("[AVFACTORY]: Client_OnSetAppearance unable to find presence for {0}", client.AgentId);
|
||||
}
|
||||
|
@ -1251,9 +1190,6 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
return;
|
||||
}
|
||||
|
||||
// we need to clean out the existing textures
|
||||
sp.Appearance.ResetAppearance();
|
||||
|
||||
// operate on a copy of the appearance so we don't have to lock anything yet
|
||||
AvatarAppearance avatAppearance = new AvatarAppearance(sp.Appearance, false);
|
||||
|
||||
|
@ -1280,15 +1216,13 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
// often sends AvatarIsWearing and SetAppearance packets at once, and AvatarIsWearing
|
||||
// shouldn't overwrite the changes made in SetAppearance.
|
||||
sp.Appearance.Wearables = avatAppearance.Wearables;
|
||||
sp.Appearance.Texture = avatAppearance.Texture;
|
||||
|
||||
// We don't need to send the appearance here since the "iswearing" will trigger a new set
|
||||
// of visual param and baked texture changes. When those complete, the new appearance will be sent
|
||||
|
||||
QueueAppearanceSave(client.AgentId);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// <summary>
|
||||
/// Respond to the cached textures request from the client
|
||||
/// </summary>
|
||||
|
@ -1308,23 +1242,9 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
|
||||
if (m_reusetextures)
|
||||
{
|
||||
// this is the most insanely dumb way to do this... however it seems to
|
||||
// actually work. if the appearance has been reset because wearables have
|
||||
// changed then the texture entries are zero'd out until the bakes are
|
||||
// uploaded. on login, if the textures exist in the cache (eg if you logged
|
||||
// into the simulator recently, then the appearance will pull those and send
|
||||
// them back in the packet and you won't have to rebake. if the textures aren't
|
||||
// in the cache then the intial makeroot() call in scenepresence will zero
|
||||
// them out.
|
||||
//
|
||||
// a better solution (though how much better is an open question) is to
|
||||
// store the hashes in the appearance and compare them. Thats's coming.
|
||||
|
||||
Primitive.TextureEntryFace face = sp.Appearance.Texture.FaceTextures[index];
|
||||
if (face != null)
|
||||
texture = face.TextureID;
|
||||
|
||||
// m_log.WarnFormat("[AVFACTORY]: reuse texture {0} for index {1}",texture,index);
|
||||
}
|
||||
|
||||
CachedTextureResponseArg response = new CachedTextureResponseArg();
|
||||
|
@ -1334,21 +1254,16 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
|
||||
cachedTextureResponse.Add(response);
|
||||
}
|
||||
|
||||
// m_log.WarnFormat("[AVFACTORY]: serial is {0}",serial);
|
||||
// The serial number appears to be used to match requests and responses
|
||||
// in the texture transaction. We just send back the serial number
|
||||
// that was provided in the request. The viewer bumps this for us.
|
||||
client.SendCachedTextureResponse(sp, serial, cachedTextureResponse);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
public void WriteBakedTexturesReport(IScenePresence sp, ReportOutputAction outputAction)
|
||||
{
|
||||
outputAction("For {0} in {1}", sp.Name, m_scene.RegionInfo.RegionName);
|
||||
outputAction(BAKED_TEXTURES_REPORT_FORMAT, "Bake Type", "UUID");
|
||||
outputAction("For {0} in {1}", null, sp.Name, m_scene.RegionInfo.RegionName);
|
||||
outputAction(BAKED_TEXTURES_REPORT_FORMAT, null, "Bake Type", "UUID");
|
||||
|
||||
Dictionary<BakeType, Primitive.TextureEntryFace> bakedTextures = GetBakedTextureFaces(sp.UUID);
|
||||
|
||||
|
@ -1362,19 +1277,38 @@ namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
|
|||
}
|
||||
else
|
||||
{
|
||||
rawTextureID = bakedTextures[bt].TextureID.ToString();
|
||||
|
||||
if (m_scene.AssetService.Get(rawTextureID) == null)
|
||||
rawTextureID += " (not found)";
|
||||
if(bakedTextures[bt].TextureID == AppearanceManager.DEFAULT_AVATAR_TEXTURE)
|
||||
rawTextureID = "not set";
|
||||
else
|
||||
rawTextureID += " (uploaded)";
|
||||
{
|
||||
rawTextureID = bakedTextures[bt].TextureID.ToString();
|
||||
|
||||
if (m_scene.AssetService.Get(rawTextureID) == null)
|
||||
rawTextureID += " (not found)";
|
||||
else
|
||||
rawTextureID += " (uploaded)";
|
||||
}
|
||||
}
|
||||
|
||||
outputAction(BAKED_TEXTURES_REPORT_FORMAT, null, bt, rawTextureID);
|
||||
}
|
||||
|
||||
bool bakedTextureValid = m_scene.AvatarFactory.ValidateBakedTextureCache(sp);
|
||||
outputAction("{0} baked appearance texture is {1}", sp.Name, bakedTextureValid ? "OK" : "incomplete");
|
||||
outputAction("{0} baked appearance texture is {1}", null, sp.Name, bakedTextureValid ? "OK" : "incomplete");
|
||||
}
|
||||
|
||||
public void SetPreferencesHoverZ(UUID agentId, float val)
|
||||
{
|
||||
ScenePresence sp = m_scene.GetScenePresence(agentId);
|
||||
if (sp == null || sp.IsDeleted || sp.IsNPC || sp.IsInTransit)
|
||||
return;
|
||||
float last = sp.Appearance.AvatarPreferencesHoverZ;
|
||||
if(val != last)
|
||||
{
|
||||
sp.Appearance.AvatarPreferencesHoverZ = val;
|
||||
//sp.SendAppearanceToAgentNF(sp);
|
||||
QueueAppearanceSend(agentId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
{
|
||||
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
|
||||
private static readonly string LogHeader = "[ENTITY TRANSFER MODULE]";
|
||||
private static readonly string OutfitTPError = "destination region does not support the Outfit you are wearing. Please retry with a simpler one";
|
||||
|
||||
public const bool WaitForAgentArrivedAtDestinationDefault = true;
|
||||
|
||||
|
@ -720,6 +721,17 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
return;
|
||||
}
|
||||
|
||||
if (!sp.Appearance.CanTeleport(ctx.OutboundVersion))
|
||||
{
|
||||
sp.ControllingClient.SendTeleportFailed(OutfitTPError);
|
||||
|
||||
m_log.DebugFormat(
|
||||
"[ENTITY TRANSFER MODULE]: {0} was stopped from teleporting from {1} to {2} because: {3}",
|
||||
sp.Name, sp.Scene.Name, finalDestination.RegionName, "incompatible wearable");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Before this point, teleport 'failure' is due to checkable pre-conditions such as whether the target
|
||||
// simulator can be found and is explicitly prepared to allow access. Therefore, we will not count these
|
||||
// as server attempts.
|
||||
|
@ -1489,6 +1501,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
m_bannedRegionCache.Add(destinyHandle, agentID, 30.0, 30.0);
|
||||
return false;
|
||||
}
|
||||
if (!agent.Appearance.CanTeleport(ctx.OutboundVersion))
|
||||
{
|
||||
reason = OutfitTPError;
|
||||
m_bannedRegionCache.Add(destinyHandle, agentID, 30.0, 30.0);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1545,7 +1564,6 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
failureReason = "Access Denied";
|
||||
return null;
|
||||
}
|
||||
|
||||
return neighbourRegion;
|
||||
}
|
||||
|
||||
|
@ -1599,9 +1617,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
agent.ControllingClient.SendAlertMessage(failureReason);
|
||||
return agent;
|
||||
}
|
||||
if (!agent.Appearance.CanTeleport(ctx.OutboundVersion))
|
||||
{
|
||||
if (agent.ControllingClient != null)
|
||||
agent.ControllingClient.SendAlertMessage(OutfitTPError);
|
||||
return agent;
|
||||
}
|
||||
|
||||
// agent.IsInTransit = true;
|
||||
|
||||
// agent.IsInTransit = true;
|
||||
CrossAgentToNewRegionAsync(agent, newpos, neighbourRegion, isFlying, ctx);
|
||||
agent.IsInTransit = false;
|
||||
return agent;
|
||||
|
@ -2601,7 +2624,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
|
|||
string reason = String.Empty;
|
||||
|
||||
EntityTransferContext ctx = new EntityTransferContext();
|
||||
bool regionAccepted = scene.SimulationService.CreateAgent(reg, reg, agentCircData, (uint)TeleportFlags.Default, ctx, out reason);
|
||||
bool regionAccepted = scene.SimulationService.CreateAgent(reg, reg, agentCircData, (uint)TeleportFlags.Default, null, out reason);
|
||||
|
||||
if (regionAccepted)
|
||||
{
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace OpenSim.Region.Framework.Interfaces
|
|||
{
|
||||
void SetAppearance(IScenePresence sp, AvatarAppearance appearance, WearableCacheItem[] cacheItems);
|
||||
void SetAppearance(IScenePresence sp, Primitive.TextureEntry textureEntry, byte[] visualParams, WearableCacheItem[] cacheItems);
|
||||
|
||||
void SetPreferencesHoverZ(UUID agentId, float val);
|
||||
/// <summary>
|
||||
/// Send the appearance of an avatar to others in the scene.
|
||||
/// </summary>
|
||||
|
|
|
@ -4358,16 +4358,7 @@ namespace OpenSim.Region.Framework.Scenes
|
|||
|
||||
public void SendAppearanceToAgentNF(ScenePresence avatar)
|
||||
{
|
||||
if(avatar.UUID == UUID)
|
||||
{
|
||||
avatar.ControllingClient.SendAppearance(
|
||||
UUID, Appearance.VisualParams, Appearance.Texture.GetBytes());
|
||||
}
|
||||
else
|
||||
{
|
||||
avatar.ControllingClient.SendAppearance(
|
||||
UUID, Appearance.VisualParams, Appearance.Texture.GetBakesBytes());
|
||||
}
|
||||
avatar.ControllingClient.SendAppearance(UUID, Appearance.VisualParams, Appearance.Texture.GetBakesBytes(), Appearance.AvatarPreferencesHoverZ);
|
||||
}
|
||||
|
||||
public void SendAnimPackToAgent(ScenePresence p)
|
||||
|
|
|
@ -961,7 +961,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
|
|||
|
||||
}
|
||||
|
||||
public void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry)
|
||||
public void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry, float hoverheight)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -413,13 +413,13 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance
|
|||
sb.AppendFormat("Wearables checks for {0}\n\n", sp.Name);
|
||||
|
||||
AvatarWearable[] wearables = sp.Appearance.Wearables;
|
||||
if(wearables.Count() == 0)
|
||||
if(wearables.Length == 0)
|
||||
{
|
||||
MainConsole.Instance.Output("avatar has no wearables");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < wearables.Count(); i++)
|
||||
for (int i = 0; i < wearables.Length; i++)
|
||||
{
|
||||
AvatarWearable aw = wearables[i];
|
||||
|
||||
|
@ -477,8 +477,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.Appearance
|
|||
cdt.AddColumn("Type", 10);
|
||||
cdt.AddColumn("Item UUID", ConsoleDisplayUtil.UuidSize);
|
||||
cdt.AddColumn("Asset UUID", ConsoleDisplayUtil.UuidSize);
|
||||
AvatarWearable[] wearables = sp.Appearance.Wearables;
|
||||
|
||||
for (int i = (int)WearableType.Shape; i < (int)WearableType.Physics; i++)
|
||||
for (int i = 0; i < wearables.Length; i++)
|
||||
{
|
||||
AvatarWearable aw = sp.Appearance.Wearables[i];
|
||||
|
||||
|
|
|
@ -664,7 +664,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
|
|||
{
|
||||
}
|
||||
|
||||
public virtual void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry)
|
||||
public virtual void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry, float hover)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ namespace OpenSim.Server.Handlers.AgentPreferences
|
|||
|
||||
AgentPrefs data = new AgentPrefs(userID);
|
||||
data.AccessPrefs = request["AccessPrefs"].ToString();
|
||||
data.HoverHeight = double.Parse(request["HoverHeight"].ToString());
|
||||
data.HoverHeight = float.Parse(request["HoverHeight"].ToString());
|
||||
data.Language = request["Language"].ToString();
|
||||
data.LanguageIsPublic = bool.Parse(request["LanguageIsPublic"].ToString());
|
||||
data.PermEveryone = int.Parse(request["PermEveryone"].ToString());
|
||||
|
|
|
@ -116,6 +116,8 @@ namespace OpenSim.Services.Connectors.Simulation
|
|||
try
|
||||
{
|
||||
OSDMap args = aCircuit.PackAgentCircuitData(ctx);
|
||||
if(ctx == null)
|
||||
ctx = new EntityTransferContext();
|
||||
args["context"] = ctx.Pack();
|
||||
PackData(args, source, aCircuit, destination, flags);
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ namespace OpenSim.Services.Interfaces
|
|||
if (kvp.ContainsKey("AccessPrefs"))
|
||||
AccessPrefs = kvp["AccessPrefs"];
|
||||
if (kvp.ContainsKey("HoverHeight"))
|
||||
HoverHeight = double.Parse(kvp["HoverHeight"]);
|
||||
HoverHeight = float.Parse(kvp["HoverHeight"]);
|
||||
if (kvp.ContainsKey("Language"))
|
||||
Language = kvp["Language"];
|
||||
if (kvp.ContainsKey("LanguageIsPublic"))
|
||||
|
@ -65,7 +65,7 @@ namespace OpenSim.Services.Interfaces
|
|||
if (kvp.ContainsKey("AccessPrefs"))
|
||||
AccessPrefs = kvp["AccessPrefs"].ToString();
|
||||
if (kvp.ContainsKey("HoverHeight"))
|
||||
HoverHeight = double.Parse(kvp["HoverHeight"].ToString());
|
||||
HoverHeight = float.Parse(kvp["HoverHeight"].ToString());
|
||||
if (kvp.ContainsKey("Language"))
|
||||
Language = kvp["Language"].ToString();
|
||||
if (kvp.ContainsKey("LanguageIsPublic"))
|
||||
|
@ -95,7 +95,7 @@ namespace OpenSim.Services.Interfaces
|
|||
public UUID PrincipalID = UUID.Zero;
|
||||
public string AccessPrefs = "M";
|
||||
//public int GodLevel; // *TODO: Implement GodLevel (Unused by the viewer, afaict - 6/11/2015)
|
||||
public double HoverHeight = 0.0;
|
||||
public float HoverHeight = 0.0f;
|
||||
public string Language = "en-us";
|
||||
public bool LanguageIsPublic = true;
|
||||
// DefaultObjectPermMasks
|
||||
|
|
|
@ -570,7 +570,7 @@ namespace OpenSim.Tests.Common
|
|||
{
|
||||
}
|
||||
|
||||
public virtual void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry)
|
||||
public virtual void SendAppearance(UUID agentID, byte[] visualParams, byte[] textureEntry, float hover)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue