add more test code to make usage od compressed updates etc. Should be disable, but well many things can go wrong.
parent
db191cd4e2
commit
d0052c8174
|
@ -602,15 +602,25 @@ namespace OpenSim.Framework
|
||||||
{
|
{
|
||||||
// we are on the new one
|
// we are on the new one
|
||||||
if (m_flags.HasFlag(PrimUpdateFlags.CancelKill))
|
if (m_flags.HasFlag(PrimUpdateFlags.CancelKill))
|
||||||
|
{
|
||||||
|
if (m_flags.HasFlag(PrimUpdateFlags.UpdateProbe))
|
||||||
|
m_flags = PrimUpdateFlags.UpdateProbe;
|
||||||
|
else
|
||||||
m_flags = PrimUpdateFlags.FullUpdatewithAnim;
|
m_flags = PrimUpdateFlags.FullUpdatewithAnim;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void Update(EntityUpdate oldupdate)
|
public virtual void Update(EntityUpdate oldupdate)
|
||||||
{
|
{
|
||||||
// we are on the new one
|
// we are on the new one
|
||||||
PrimUpdateFlags updateFlags = oldupdate.Flags;
|
PrimUpdateFlags updateFlags = oldupdate.Flags;
|
||||||
|
if (updateFlags.HasFlag(PrimUpdateFlags.UpdateProbe))
|
||||||
|
updateFlags &= ~PrimUpdateFlags.UpdateProbe;
|
||||||
if (m_flags.HasFlag(PrimUpdateFlags.CancelKill))
|
if (m_flags.HasFlag(PrimUpdateFlags.CancelKill))
|
||||||
{
|
{
|
||||||
|
if(m_flags.HasFlag(PrimUpdateFlags.UpdateProbe))
|
||||||
|
m_flags = PrimUpdateFlags.UpdateProbe;
|
||||||
|
else
|
||||||
m_flags = PrimUpdateFlags.FullUpdatewithAnim;
|
m_flags = PrimUpdateFlags.FullUpdatewithAnim;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -679,6 +689,7 @@ namespace OpenSim.Framework
|
||||||
|
|
||||||
FullUpdatewithAnim = FullUpdate | Animations,
|
FullUpdatewithAnim = FullUpdate | Animations,
|
||||||
|
|
||||||
|
UpdateProbe = 0x10000000, // 1 << 28
|
||||||
SendInTransit = 0x20000000, // 1 << 29
|
SendInTransit = 0x20000000, // 1 << 29
|
||||||
CancelKill = 0x40000000, // 1 << 30
|
CancelKill = 0x40000000, // 1 << 30
|
||||||
Kill = 0x80000000 // 1 << 31
|
Kill = 0x80000000 // 1 << 31
|
||||||
|
@ -805,7 +816,7 @@ namespace OpenSim.Framework
|
||||||
event TeleportCancel OnTeleportCancel;
|
event TeleportCancel OnTeleportCancel;
|
||||||
event DeRezObject OnDeRezObject;
|
event DeRezObject OnDeRezObject;
|
||||||
event RezRestoreToWorld OnRezRestoreToWorld;
|
event RezRestoreToWorld OnRezRestoreToWorld;
|
||||||
event Action<IClientAPI> OnRegionHandShakeReply;
|
event Action<IClientAPI, uint> OnRegionHandShakeReply;
|
||||||
event GenericCall1 OnRequestWearables;
|
event GenericCall1 OnRequestWearables;
|
||||||
event Action<IClientAPI, bool> OnCompleteMovementToRegion;
|
event Action<IClientAPI, bool> OnCompleteMovementToRegion;
|
||||||
|
|
||||||
|
|
|
@ -80,7 +80,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
public event DeRezObject OnDeRezObject;
|
public event DeRezObject OnDeRezObject;
|
||||||
public event RezRestoreToWorld OnRezRestoreToWorld;
|
public event RezRestoreToWorld OnRezRestoreToWorld;
|
||||||
public event ModifyTerrain OnModifyTerrain;
|
public event ModifyTerrain OnModifyTerrain;
|
||||||
public event Action<IClientAPI> OnRegionHandShakeReply;
|
public event Action<IClientAPI, uint> OnRegionHandShakeReply;
|
||||||
public event GenericCall1 OnRequestWearables;
|
public event GenericCall1 OnRequestWearables;
|
||||||
public event SetAppearance OnSetAppearance;
|
public event SetAppearance OnSetAppearance;
|
||||||
public event AvatarNowWearing OnAvatarNowWearing;
|
public event AvatarNowWearing OnAvatarNowWearing;
|
||||||
|
@ -392,6 +392,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
protected IAssetService m_assetService;
|
protected IAssetService m_assetService;
|
||||||
|
|
||||||
|
protected bool m_supportViewerCache = false;
|
||||||
#endregion Class Members
|
#endregion Class Members
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
|
@ -552,6 +553,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
string name = string.Format("AsyncInUDP-{0}",m_agentId.ToString());
|
string name = string.Format("AsyncInUDP-{0}",m_agentId.ToString());
|
||||||
m_asyncPacketProcess = new JobEngine(name, name, 10000);
|
m_asyncPacketProcess = new JobEngine(name, name, 10000);
|
||||||
IsActive = true;
|
IsActive = true;
|
||||||
|
|
||||||
|
m_supportViewerCache = m_udpServer.SupportViewerObjectsCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Client Methods
|
#region Client Methods
|
||||||
|
@ -4777,6 +4780,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
13 // ID (high frequency)
|
13 // ID (high frequency)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static private readonly byte[] ObjectUpdateCachedHeader = new byte[] {
|
||||||
|
Helpers.MSG_RELIABLE,
|
||||||
|
0, 0, 0, 0, // sequence number
|
||||||
|
0, // extra
|
||||||
|
14 // ID (high frequency)
|
||||||
|
};
|
||||||
|
|
||||||
private void ProcessEntityUpdates(int maxUpdatesBytes)
|
private void ProcessEntityUpdates(int maxUpdatesBytes)
|
||||||
{
|
{
|
||||||
if (!IsActive)
|
if (!IsActive)
|
||||||
|
@ -4786,8 +4796,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
if (mysp == null)
|
if (mysp == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
||||||
List<EntityUpdate> objectUpdates = null;
|
List<EntityUpdate> objectUpdates = null;
|
||||||
//List<EntityUpdate> compressedUpdates = null;
|
List<EntityUpdate> objectUpdateProbes = null;
|
||||||
|
List<EntityUpdate> compressedUpdates = null;
|
||||||
List<EntityUpdate> terseUpdates = null;
|
List<EntityUpdate> terseUpdates = null;
|
||||||
List<SceneObjectPart> ObjectAnimationUpdates = null;
|
List<SceneObjectPart> ObjectAnimationUpdates = null;
|
||||||
|
|
||||||
|
@ -4799,6 +4811,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
EntityUpdate update;
|
EntityUpdate update;
|
||||||
|
|
||||||
|
bool viewerCache = m_supportViewerCache && (m_viewerHandShakeFlags & 1) != 0 && mysp.IsChildAgent; // only on child agents
|
||||||
bool doCulling = m_scene.ObjectsCullingByDistance;
|
bool doCulling = m_scene.ObjectsCullingByDistance;
|
||||||
float cullingrange = 64.0f;
|
float cullingrange = 64.0f;
|
||||||
Vector3 mypos = Vector3.Zero;
|
Vector3 mypos = Vector3.Zero;
|
||||||
|
@ -4807,7 +4820,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
bool orderedDequeue = false; // temporary off
|
bool orderedDequeue = false; // temporary off
|
||||||
|
|
||||||
HashSet<SceneObjectGroup> GroupsNeedFullUpdate = new HashSet<SceneObjectGroup>();
|
HashSet<SceneObjectGroup> GroupsNeedFullUpdate = new HashSet<SceneObjectGroup>();
|
||||||
|
bool useCompressUpdate = false;
|
||||||
|
|
||||||
if (doCulling)
|
if (doCulling)
|
||||||
{
|
{
|
||||||
|
@ -4834,15 +4847,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrimUpdateFlags updateFlags = (PrimUpdateFlags)update.Flags;
|
PrimUpdateFlags updateFlags = update.Flags;
|
||||||
|
|
||||||
if(updateFlags.HasFlag(PrimUpdateFlags.Kill))
|
if (updateFlags.HasFlag(PrimUpdateFlags.Kill))
|
||||||
{
|
{
|
||||||
m_killRecord.Add(update.Entity.LocalId);
|
m_killRecord.Add(update.Entity.LocalId);
|
||||||
maxUpdatesBytes -= 30;
|
maxUpdatesBytes -= 30;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useCompressUpdate = false;
|
||||||
|
|
||||||
if (update.Entity is SceneObjectPart)
|
if (update.Entity is SceneObjectPart)
|
||||||
{
|
{
|
||||||
SceneObjectPart part = (SceneObjectPart)update.Entity;
|
SceneObjectPart part = (SceneObjectPart)update.Entity;
|
||||||
|
@ -4928,11 +4943,39 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
if (dpos > maxview * maxview)
|
if (dpos > maxview * maxview)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (!viewerCache || !updateFlags.HasFlag(PrimUpdateFlags.UpdateProbe))
|
||||||
|
{
|
||||||
GroupsNeedFullUpdate.Add(grp);
|
GroupsNeedFullUpdate.Add(grp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (updateFlags.HasFlag(PrimUpdateFlags.UpdateProbe))
|
||||||
|
{
|
||||||
|
if (objectUpdateProbes == null)
|
||||||
|
{
|
||||||
|
objectUpdateProbes = new List<EntityUpdate>();
|
||||||
|
maxUpdatesBytes -= 18;
|
||||||
|
}
|
||||||
|
objectUpdateProbes.Add(update);
|
||||||
|
maxUpdatesBytes -= 12;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_SupportObjectAnimations && updateFlags.HasFlag(PrimUpdateFlags.Animations))
|
||||||
|
{
|
||||||
|
if (part.Animations != null)
|
||||||
|
{
|
||||||
|
if (ObjectAnimationUpdates == null)
|
||||||
|
ObjectAnimationUpdates = new List<SceneObjectPart>();
|
||||||
|
ObjectAnimationUpdates.Add(part);
|
||||||
|
maxUpdatesBytes -= 20 * part.Animations.Count + 24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(viewerCache)
|
||||||
|
useCompressUpdate = grp.IsViewerCachable;
|
||||||
|
}
|
||||||
else if (update.Entity is ScenePresence)
|
else if (update.Entity is ScenePresence)
|
||||||
{
|
{
|
||||||
ScenePresence presence = (ScenePresence)update.Entity;
|
ScenePresence presence = (ScenePresence)update.Entity;
|
||||||
|
@ -4951,27 +4994,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
#region UpdateFlags to packet type conversion
|
#region UpdateFlags to packet type conversion
|
||||||
|
|
||||||
// bool canUseCompressed = true;
|
|
||||||
|
|
||||||
if (update.Entity is SceneObjectPart)
|
|
||||||
{
|
|
||||||
if (m_SupportObjectAnimations && updateFlags.HasFlag(PrimUpdateFlags.Animations))
|
|
||||||
{
|
|
||||||
SceneObjectPart sop = (SceneObjectPart)update.Entity;
|
|
||||||
if ( sop.Animations != null)
|
|
||||||
{
|
|
||||||
if(ObjectAnimationUpdates == null)
|
|
||||||
ObjectAnimationUpdates = new List<SceneObjectPart>();
|
|
||||||
ObjectAnimationUpdates.Add(sop);
|
|
||||||
maxUpdatesBytes -= 20 * sop.Animations.Count + 24;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// canUseCompressed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFlags &= PrimUpdateFlags.FullUpdate; // clear other control bits already handled
|
updateFlags &= PrimUpdateFlags.FullUpdate; // clear other control bits already handled
|
||||||
if(updateFlags == PrimUpdateFlags.None)
|
if(updateFlags == PrimUpdateFlags.None)
|
||||||
continue;
|
continue;
|
||||||
|
@ -5025,23 +5047,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SceneObjectPart part = (SceneObjectPart)update.Entity;
|
if (useCompressUpdate)
|
||||||
SceneObjectGroup grp = part.ParentGroup;
|
|
||||||
// minimal compress conditions, not enough ?
|
|
||||||
//if (grp.UsesPhysics || part.Velocity.LengthSquared() > 1e-8f || part.Acceleration.LengthSquared() > 1e-6f)
|
|
||||||
{
|
|
||||||
maxUpdatesBytes -= 150; // crude estimation
|
|
||||||
|
|
||||||
if (objectUpdates == null)
|
|
||||||
{
|
|
||||||
objectUpdates = new List<EntityUpdate>();
|
|
||||||
maxUpdatesBytes -= 18;
|
|
||||||
}
|
|
||||||
objectUpdates.Add(update);
|
|
||||||
}
|
|
||||||
//compress still disabled
|
|
||||||
/*
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
maxUpdatesBytes -= 150; // crude estimation
|
maxUpdatesBytes -= 150; // crude estimation
|
||||||
|
|
||||||
|
@ -5052,7 +5058,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
}
|
}
|
||||||
compressedUpdates.Add(update);
|
compressedUpdates.Add(update);
|
||||||
}
|
}
|
||||||
*/
|
else
|
||||||
|
{
|
||||||
|
maxUpdatesBytes -= 150; // crude estimation
|
||||||
|
|
||||||
|
if (objectUpdates == null)
|
||||||
|
{
|
||||||
|
objectUpdates = new List<EntityUpdate>();
|
||||||
|
maxUpdatesBytes -= 18;
|
||||||
|
}
|
||||||
|
objectUpdates.Add(update);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5147,7 +5163,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false);
|
delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
if(compressedUpdates != null)
|
if(compressedUpdates != null)
|
||||||
{
|
{
|
||||||
List<EntityUpdate> tau = new List<EntityUpdate>(30);
|
List<EntityUpdate> tau = new List<EntityUpdate>(30);
|
||||||
|
@ -5168,8 +5184,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
int count = 0;
|
int count = 0;
|
||||||
foreach (EntityUpdate eu in compressedUpdates)
|
foreach (EntityUpdate eu in compressedUpdates)
|
||||||
{
|
{
|
||||||
|
SceneObjectPart sop = (SceneObjectPart)eu.Entity;
|
||||||
|
if (sop.ParentGroup == null || sop.ParentGroup.IsDeleted)
|
||||||
|
continue;
|
||||||
lastpos = pos;
|
lastpos = pos;
|
||||||
CreateCompressedUpdateBlock((SceneObjectPart)eu.Entity, mysp, data, ref pos);
|
CreateCompressedUpdateBlock(sop, mysp, data, ref pos);
|
||||||
if (pos < LLUDPServer.MAXPAYLOAD)
|
if (pos < LLUDPServer.MAXPAYLOAD)
|
||||||
{
|
{
|
||||||
tau.Add(eu);
|
tau.Add(eu);
|
||||||
|
@ -5207,7 +5226,68 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false);
|
delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
if (objectUpdateProbes != null)
|
||||||
|
{
|
||||||
|
UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint);
|
||||||
|
byte[] data = buf.Data;
|
||||||
|
|
||||||
|
Buffer.BlockCopy(ObjectUpdateCachedHeader, 0, data, 0, 7);
|
||||||
|
|
||||||
|
Utils.UInt64ToBytesSafepos(m_scene.RegionInfo.RegionHandle, data, 7); // 15
|
||||||
|
Utils.UInt16ToBytes(timeDilation, data, 15); // 17
|
||||||
|
|
||||||
|
int countposition = 17; // blocks count position
|
||||||
|
int pos = 18;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
foreach (EntityUpdate eu in objectUpdateProbes)
|
||||||
|
{
|
||||||
|
SceneObjectPart sop = (SceneObjectPart)eu.Entity;
|
||||||
|
if (sop.ParentGroup == null || sop.ParentGroup.IsDeleted)
|
||||||
|
continue;
|
||||||
|
uint primflags = m_scene.Permissions.GenerateClientFlags(sop, mysp);
|
||||||
|
if (mysp.UUID != sop.OwnerID)
|
||||||
|
primflags &= ~(uint)PrimFlags.CreateSelected;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (sop.CreateSelected)
|
||||||
|
primflags |= (uint)PrimFlags.CreateSelected;
|
||||||
|
else
|
||||||
|
primflags &= ~(uint)PrimFlags.CreateSelected;
|
||||||
|
}
|
||||||
|
|
||||||
|
Utils.UIntToBytes(sop.LocalId, data, pos); pos += 4;
|
||||||
|
Utils.UIntToBytes((uint)sop.ParentGroup.PseudoCRC, data, pos); pos += 4; //WRONG
|
||||||
|
Utils.UIntToBytes(primflags, data, pos); pos += 4;
|
||||||
|
|
||||||
|
if (pos < (LLUDPServer.MAXPAYLOAD - 12))
|
||||||
|
++count;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// we need more packets
|
||||||
|
UDPPacketBuffer newbuf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint);
|
||||||
|
Buffer.BlockCopy(buf.Data, 0, newbuf.Data, 0, countposition); // start is the same
|
||||||
|
|
||||||
|
buf.Data[countposition] = (byte)count;
|
||||||
|
buf.DataLength = pos;
|
||||||
|
m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, null, false, false);
|
||||||
|
|
||||||
|
buf = newbuf;
|
||||||
|
data = buf.Data;
|
||||||
|
pos = 18;
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0)
|
||||||
|
{
|
||||||
|
buf.Data[countposition] = (byte)count;
|
||||||
|
buf.DataLength = pos;
|
||||||
|
m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, null, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (terseUpdates != null)
|
if (terseUpdates != null)
|
||||||
{
|
{
|
||||||
int blocks = terseUpdates.Count;
|
int blocks = terseUpdates.Count;
|
||||||
|
@ -5329,8 +5409,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
{
|
{
|
||||||
lock (GroupsInView)
|
lock (GroupsInView)
|
||||||
GroupsInView.Add(grp);
|
GroupsInView.Add(grp);
|
||||||
|
PrimUpdateFlags flags = PrimUpdateFlags.CancelKill;
|
||||||
|
if(viewerCache && grp.IsViewerCachable)
|
||||||
|
flags |= PrimUpdateFlags.UpdateProbe;
|
||||||
foreach (SceneObjectPart p in grp.Parts)
|
foreach (SceneObjectPart p in grp.Parts)
|
||||||
SendEntityUpdate(p, PrimUpdateFlags.CancelKill);
|
SendEntityUpdate(p, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5461,10 +5544,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
if(GroupsNeedFullUpdate.Count > 0)
|
if(GroupsNeedFullUpdate.Count > 0)
|
||||||
{
|
{
|
||||||
foreach(SceneObjectGroup grp in GroupsNeedFullUpdate)
|
bool viewerCache = m_supportViewerCache && (m_viewerHandShakeFlags & 1) != 0 && mysp.IsChildAgent;
|
||||||
|
foreach (SceneObjectGroup grp in GroupsNeedFullUpdate)
|
||||||
{
|
{
|
||||||
foreach(SceneObjectPart p in grp.Parts)
|
PrimUpdateFlags flags = PrimUpdateFlags.CancelKill;
|
||||||
SendEntityUpdate(p, PrimUpdateFlags.CancelKill);
|
if (viewerCache && grp.IsViewerCachable)
|
||||||
|
flags |= PrimUpdateFlags.UpdateProbe;
|
||||||
|
foreach (SceneObjectPart p in grp.Parts)
|
||||||
|
SendEntityUpdate(p, flags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7173,7 +7260,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
dest[pos++] = state;
|
dest[pos++] = state;
|
||||||
|
|
||||||
///**** temp hack
|
///**** temp hack
|
||||||
Utils.UIntToBytesSafepos((uint)rnd.Next(), dest, pos); pos += 4; //CRC needs fix or things will get crazy for now avoid caching
|
Utils.UIntToBytesSafepos((uint)part.ParentGroup.PseudoCRC, dest, pos); pos += 4;
|
||||||
dest[pos++] = part.Material;
|
dest[pos++] = part.Material;
|
||||||
dest[pos++] = part.ClickAction;
|
dest[pos++] = part.ClickAction;
|
||||||
part.Shape.Scale.ToBytes(dest, pos); pos += 12;
|
part.Shape.Scale.ToBytes(dest, pos); pos += 12;
|
||||||
|
@ -8407,13 +8494,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public uint m_viewerHandShakeFlags = 0;
|
||||||
|
|
||||||
private bool HandlerRegionHandshakeReply(IClientAPI sender, Packet Pack)
|
private bool HandlerRegionHandshakeReply(IClientAPI sender, Packet Pack)
|
||||||
{
|
{
|
||||||
Action<IClientAPI> handlerRegionHandShakeReply = OnRegionHandShakeReply;
|
Action<IClientAPI, uint> handlerRegionHandShakeReply = OnRegionHandShakeReply;
|
||||||
if (handlerRegionHandShakeReply != null)
|
if (handlerRegionHandShakeReply == null)
|
||||||
{
|
return true; // silence the warning
|
||||||
handlerRegionHandShakeReply(this);
|
|
||||||
}
|
RegionHandshakeReplyPacket rsrpkt = (RegionHandshakeReplyPacket)Pack;
|
||||||
|
if(rsrpkt.AgentData.AgentID != m_agentId || rsrpkt.AgentData.SessionID != m_sessionId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// regionHandSHake is a protocol message, but it is also seems to be the only way to update terrain textures
|
||||||
|
// in last case this should be ignored.
|
||||||
|
OnRegionHandShakeReply = null;
|
||||||
|
if(m_supportViewerCache)
|
||||||
|
m_viewerHandShakeFlags = rsrpkt.RegionInfo.Flags;
|
||||||
|
else
|
||||||
|
m_viewerHandShakeFlags = 0;
|
||||||
|
|
||||||
|
handlerRegionHandShakeReply(this, m_viewerHandShakeFlags);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -8659,17 +8760,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
private bool HandleCompleteAgentMovement(IClientAPI sender, Packet Pack)
|
private bool HandleCompleteAgentMovement(IClientAPI sender, Packet Pack)
|
||||||
{
|
{
|
||||||
m_log.DebugFormat("[LLClientView] HandleCompleteAgentMovement");
|
//m_log.DebugFormat("[LLClientView] HandleCompleteAgentMovement");
|
||||||
|
|
||||||
Action<IClientAPI, bool> handlerCompleteMovementToRegion = OnCompleteMovementToRegion;
|
Action<IClientAPI, bool> handlerCompleteMovementToRegion = OnCompleteMovementToRegion;
|
||||||
if (handlerCompleteMovementToRegion != null)
|
if (handlerCompleteMovementToRegion == null)
|
||||||
{
|
return false;
|
||||||
handlerCompleteMovementToRegion(sender, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_log.Debug("HandleCompleteAgentMovement NULL handler");
|
|
||||||
|
|
||||||
handlerCompleteMovementToRegion = null;
|
CompleteAgentMovementPacket cmp = (CompleteAgentMovementPacket)Pack;
|
||||||
|
if(cmp.AgentData.AgentID != m_agentId || cmp.AgentData.SessionID != m_sessionId || cmp.AgentData.CircuitCode != m_circuitCode)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
handlerCompleteMovementToRegion(sender, true);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -9141,6 +9242,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
|
|
||||||
private bool HandleRequestMultipleObjects(IClientAPI sender, Packet Pack)
|
private bool HandleRequestMultipleObjects(IClientAPI sender, Packet Pack)
|
||||||
{
|
{
|
||||||
|
ObjectRequest handlerObjectRequest = OnObjectRequest;
|
||||||
|
if (handlerObjectRequest == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
RequestMultipleObjectsPacket incomingRequest = (RequestMultipleObjectsPacket)Pack;
|
RequestMultipleObjectsPacket incomingRequest = (RequestMultipleObjectsPacket)Pack;
|
||||||
|
|
||||||
#region Packet Session and User Check
|
#region Packet Session and User Check
|
||||||
|
@ -9149,16 +9254,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
return true;
|
return true;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
ObjectRequest handlerObjectRequest = null;
|
|
||||||
|
|
||||||
for (int i = 0; i < incomingRequest.ObjectData.Length; i++)
|
for (int i = 0; i < incomingRequest.ObjectData.Length; i++)
|
||||||
{
|
|
||||||
handlerObjectRequest = OnObjectRequest;
|
|
||||||
if (handlerObjectRequest != null)
|
|
||||||
{
|
|
||||||
handlerObjectRequest(incomingRequest.ObjectData[i].ID, this);
|
handlerObjectRequest(incomingRequest.ObjectData[i].ID, this);
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -369,6 +369,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int IncomingOrphanedPacketCount { get; protected set; }
|
public int IncomingOrphanedPacketCount { get; protected set; }
|
||||||
|
|
||||||
|
public bool SupportViewerObjectsCache = false;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Run queue empty processing within a single persistent thread.
|
/// Run queue empty processing within a single persistent thread.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -433,6 +434,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
m_disableFacelights = config.GetBoolean("DisableFacelights", false);
|
m_disableFacelights = config.GetBoolean("DisableFacelights", false);
|
||||||
m_ackTimeout = 1000 * config.GetInt("AckTimeout", 60);
|
m_ackTimeout = 1000 * config.GetInt("AckTimeout", 60);
|
||||||
m_pausedAckTimeout = 1000 * config.GetInt("PausedAckTimeout", 300);
|
m_pausedAckTimeout = 1000 * config.GetInt("PausedAckTimeout", 300);
|
||||||
|
SupportViewerObjectsCache = config.GetBoolean("SupportViewerObjectsCache", SupportViewerObjectsCache);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1724,14 +1726,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP
|
||||||
if (client != null)
|
if (client != null)
|
||||||
{
|
{
|
||||||
client.SendRegionHandshake();
|
client.SendRegionHandshake();
|
||||||
|
|
||||||
client.CheckViewerCaps();
|
client.CheckViewerCaps();
|
||||||
|
|
||||||
// We only want to send initial data to new clients, not ones which are being converted from child to root.
|
|
||||||
bool tp = (aCircuit.teleportFlags > 0);
|
|
||||||
// Let's delay this for TP agents, otherwise the viewer doesn't know where to get resources from
|
|
||||||
if (!tp)
|
|
||||||
client.SceneAgent.SendInitialDataToMe();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -574,7 +574,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
|
||||||
Scene.RegionInfo.RegionSettings.Save();
|
Scene.RegionInfo.RegionSettings.Save();
|
||||||
TriggerRegionInfoChange();
|
TriggerRegionInfoChange();
|
||||||
sendRegionHandshakeToAll();
|
sendRegionHandshakeToAll();
|
||||||
sendRegionInfoPacketToAll();
|
// sendRegionInfoPacketToAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleCommitEstateTerrainTextureRequest(IClientAPI remoteClient)
|
private void handleCommitEstateTerrainTextureRequest(IClientAPI remoteClient)
|
||||||
|
|
|
@ -153,10 +153,23 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
/// <param name="remoteClient"></param>
|
/// <param name="remoteClient"></param>
|
||||||
public void RequestPrim(uint primLocalID, IClientAPI remoteClient)
|
public void RequestPrim(uint primLocalID, IClientAPI remoteClient)
|
||||||
{
|
{
|
||||||
SceneObjectGroup sog = GetGroupByPrim(primLocalID);
|
SceneObjectPart part = GetSceneObjectPart(primLocalID);
|
||||||
|
if (part != null)
|
||||||
|
{
|
||||||
|
SceneObjectGroup sog = part.ParentGroup;
|
||||||
|
if(!sog.IsDeleted)
|
||||||
|
{
|
||||||
|
PrimUpdateFlags update = PrimUpdateFlags.FullUpdate;
|
||||||
|
if (sog.RootPart.Shape.MeshFlagEntry)
|
||||||
|
update = PrimUpdateFlags.FullUpdatewithAnim;
|
||||||
|
part.SendUpdate(remoteClient, update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (sog != null)
|
//SceneObjectGroup sog = GetGroupByPrim(primLocalID);
|
||||||
sog.SendFullAnimUpdateToClient(remoteClient);
|
|
||||||
|
//if (sog != null)
|
||||||
|
//sog.SendFullAnimUpdateToClient(remoteClient);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -170,8 +170,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
private bool m_scripts_enabled;
|
private bool m_scripts_enabled;
|
||||||
|
|
||||||
public SynchronizeSceneHandler SynchronizeScene;
|
|
||||||
|
|
||||||
public bool ClampNegativeZ
|
public bool ClampNegativeZ
|
||||||
{
|
{
|
||||||
get { return m_clampNegativeZ; }
|
get { return m_clampNegativeZ; }
|
||||||
|
@ -1006,11 +1004,9 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
m_useTrashOnDelete = startupConfig.GetBoolean("UseTrashOnDelete",m_useTrashOnDelete);
|
m_useTrashOnDelete = startupConfig.GetBoolean("UseTrashOnDelete",m_useTrashOnDelete);
|
||||||
m_trustBinaries = startupConfig.GetBoolean("TrustBinaries", m_trustBinaries);
|
m_trustBinaries = startupConfig.GetBoolean("TrustBinaries", m_trustBinaries);
|
||||||
m_allowScriptCrossings = startupConfig.GetBoolean("AllowScriptCrossing", m_allowScriptCrossings);
|
m_allowScriptCrossings = startupConfig.GetBoolean("AllowScriptCrossing", m_allowScriptCrossings);
|
||||||
m_dontPersistBefore =
|
m_dontPersistBefore = startupConfig.GetLong("MinimumTimeBeforePersistenceConsidered", DEFAULT_MIN_TIME_FOR_PERSISTENCE);
|
||||||
startupConfig.GetLong("MinimumTimeBeforePersistenceConsidered", DEFAULT_MIN_TIME_FOR_PERSISTENCE);
|
|
||||||
m_dontPersistBefore *= 10000000;
|
m_dontPersistBefore *= 10000000;
|
||||||
m_persistAfter =
|
m_persistAfter = startupConfig.GetLong("MaximumTimeBeforePersistenceConsidered", DEFAULT_MAX_TIME_FOR_PERSISTENCE);
|
||||||
startupConfig.GetLong("MaximumTimeBeforePersistenceConsidered", DEFAULT_MAX_TIME_FOR_PERSISTENCE);
|
|
||||||
m_persistAfter *= 10000000;
|
m_persistAfter *= 10000000;
|
||||||
|
|
||||||
m_defaultScriptEngine = startupConfig.GetString("DefaultScriptEngine", "XEngine");
|
m_defaultScriptEngine = startupConfig.GetString("DefaultScriptEngine", "XEngine");
|
||||||
|
@ -1695,9 +1691,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
{
|
{
|
||||||
if (PhysicsEnabled)
|
if (PhysicsEnabled)
|
||||||
physicsFPS = m_sceneGraph.UpdatePhysics(FrameTime);
|
physicsFPS = m_sceneGraph.UpdatePhysics(FrameTime);
|
||||||
|
|
||||||
if (SynchronizeScene != null)
|
|
||||||
SynchronizeScene(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpMS2 = Util.GetTimeStampMS();
|
tmpMS2 = Util.GetTimeStampMS();
|
||||||
|
@ -1775,30 +1768,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
// Region ready should always be set
|
// Region ready should always be set
|
||||||
Ready = true;
|
Ready = true;
|
||||||
|
|
||||||
|
|
||||||
IConfig restartConfig = m_config.Configs["RestartModule"];
|
|
||||||
if (restartConfig != null)
|
|
||||||
{
|
|
||||||
string markerPath = restartConfig.GetString("MarkerPath", String.Empty);
|
|
||||||
|
|
||||||
if (markerPath != String.Empty)
|
|
||||||
{
|
|
||||||
string path = Path.Combine(markerPath, RegionInfo.RegionID.ToString() + ".ready");
|
|
||||||
try
|
|
||||||
{
|
|
||||||
string pidstring = System.Diagnostics.Process.GetCurrentProcess().Id.ToString();
|
|
||||||
FileStream fs = File.Create(path);
|
|
||||||
System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
|
|
||||||
Byte[] buf = enc.GetBytes(pidstring);
|
|
||||||
fs.Write(buf, 0, buf.Length);
|
|
||||||
fs.Close();
|
|
||||||
}
|
|
||||||
catch (Exception)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -119,6 +119,21 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
// private PrimCountTaintedDelegate handlerPrimCountTainted = null;
|
// private PrimCountTaintedDelegate handlerPrimCountTainted = null;
|
||||||
|
|
||||||
|
public bool IsViewerCachable
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
// needs more exclusion ?
|
||||||
|
return(Backup && !IsTemporary && !inTransit && !IsSelected && !UsesPhysics && !IsAttachmentCheckFull() &&
|
||||||
|
!RootPart.Shape.MeshFlagEntry && // animations are not sent correctly for now
|
||||||
|
RootPart.KeyframeMotion == null &&
|
||||||
|
(DateTime.UtcNow.Ticks - timeLastChanged > 36000000000) && //36000000000 is one hour
|
||||||
|
RootPart.Velocity.LengthSquared() < 1e8f && // should not be needed
|
||||||
|
RootPart.Acceleration.LengthSquared() < 1e4f // should not be needed
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Signal whether the non-inventory attributes of any prims in the group have changed
|
/// Signal whether the non-inventory attributes of any prims in the group have changed
|
||||||
/// since the group's last persistent backup
|
/// since the group's last persistent backup
|
||||||
|
@ -128,7 +143,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
private long timeLastChanged = 0;
|
private long timeLastChanged = 0;
|
||||||
private long m_maxPersistTime = 0;
|
private long m_maxPersistTime = 0;
|
||||||
private long m_minPersistTime = 0;
|
private long m_minPersistTime = 0;
|
||||||
// private Random m_rand;
|
|
||||||
|
public int PseudoCRC;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This indicates whether the object has changed such that it needs to be repersisted to permenant storage
|
/// This indicates whether the object has changed such that it needs to be repersisted to permenant storage
|
||||||
|
@ -145,40 +161,26 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
{
|
{
|
||||||
if (value)
|
if (value)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (Backup)
|
if (Backup)
|
||||||
{
|
|
||||||
m_scene.SceneGraph.FireChangeBackup(this);
|
m_scene.SceneGraph.FireChangeBackup(this);
|
||||||
}
|
|
||||||
|
PseudoCRC = (int)(DateTime.UtcNow.Ticks); ;
|
||||||
timeLastChanged = DateTime.UtcNow.Ticks;
|
timeLastChanged = DateTime.UtcNow.Ticks;
|
||||||
if (!m_hasGroupChanged)
|
if (!m_hasGroupChanged)
|
||||||
timeFirstChanged = DateTime.UtcNow.Ticks;
|
timeFirstChanged = timeLastChanged;
|
||||||
if (m_rootPart != null && m_scene != null)
|
if (m_rootPart != null && m_scene != null)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
if (m_rand == null)
|
|
||||||
{
|
|
||||||
byte[] val = new byte[16];
|
|
||||||
m_rootPart.UUID.ToBytes(val, 0);
|
|
||||||
m_rand = new Random(BitConverter.ToInt32(val, 0));
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (m_scene.GetRootAgentCount() == 0)
|
if (m_scene.GetRootAgentCount() == 0)
|
||||||
{
|
{
|
||||||
//If the region is empty, this change has been made by an automated process
|
//If the region is empty, this change has been made by an automated process
|
||||||
//and thus we delay the persist time by a random amount between 1.5 and 2.5.
|
//and thus we delay the persist time by a random amount between 1.5 and 2.5.
|
||||||
|
|
||||||
// float factor = 1.5f + (float)(m_rand.NextDouble());
|
|
||||||
float factor = 2.0f;
|
float factor = 2.0f;
|
||||||
m_maxPersistTime = (long)((float)m_scene.m_persistAfter * factor);
|
m_maxPersistTime = (long)(m_scene.m_persistAfter * factor);
|
||||||
m_minPersistTime = (long)((float)m_scene.m_dontPersistBefore * factor);
|
m_minPersistTime = (long)(m_scene.m_dontPersistBefore * factor);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//If the region is not empty, we want to obey the minimum and maximum persist times
|
|
||||||
//but add a random factor so we stagger the object persistance a little
|
|
||||||
// m_maxPersistTime = (long)((float)m_scene.m_persistAfter * (1.0d - (m_rand.NextDouble() / 5.0d))); //Multiply by 1.0-1.5
|
|
||||||
// m_minPersistTime = (long)((float)m_scene.m_dontPersistBefore * (1.0d + (m_rand.NextDouble() / 2.0d))); //Multiply by 0.8-1.0
|
|
||||||
m_maxPersistTime = m_scene.m_persistAfter;
|
m_maxPersistTime = m_scene.m_persistAfter;
|
||||||
m_minPersistTime = m_scene.m_dontPersistBefore;
|
m_minPersistTime = m_scene.m_dontPersistBefore;
|
||||||
}
|
}
|
||||||
|
@ -1330,6 +1332,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
public SceneObjectGroup()
|
public SceneObjectGroup()
|
||||||
{
|
{
|
||||||
m_lastCollisionSoundMS = Util.GetTimeStampMS() + 1000.0;
|
m_lastCollisionSoundMS = Util.GetTimeStampMS() + 1000.0;
|
||||||
|
PseudoCRC = (int)(DateTime.UtcNow.Ticks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -2441,6 +2444,21 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SendUpdateProbes(IClientAPI remoteClient)
|
||||||
|
{
|
||||||
|
PrimUpdateFlags update = PrimUpdateFlags.UpdateProbe;
|
||||||
|
|
||||||
|
RootPart.SendUpdate(remoteClient, update);
|
||||||
|
|
||||||
|
SceneObjectPart[] parts = m_parts.GetArray();
|
||||||
|
for (int i = 0; i < parts.Length; i++)
|
||||||
|
{
|
||||||
|
SceneObjectPart part = parts[i];
|
||||||
|
if (part != RootPart)
|
||||||
|
part.SendUpdate(remoteClient, update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#region Copying
|
#region Copying
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -2516,6 +2534,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
|
|
||||||
dupe.InvalidatePartsLinkMaps();
|
dupe.InvalidatePartsLinkMaps();
|
||||||
|
dupe.PseudoCRC = (int)(DateTime.UtcNow.Ticks);
|
||||||
m_dupeInProgress = false;
|
m_dupeInProgress = false;
|
||||||
return dupe;
|
return dupe;
|
||||||
}
|
}
|
||||||
|
@ -2769,6 +2788,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PseudoCRC = (int)(DateTime.UtcNow.Ticks);
|
||||||
rpart.ScheduleFullUpdate();
|
rpart.ScheduleFullUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2808,6 +2828,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
part.ResetIDs(part.LinkNum); // Don't change link nums
|
part.ResetIDs(part.LinkNum); // Don't change link nums
|
||||||
m_parts.Add(part.UUID, part);
|
m_parts.Add(part.UUID, part);
|
||||||
}
|
}
|
||||||
|
PseudoCRC = (int)(DateTime.UtcNow.Ticks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3117,7 +3138,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 'linkPart' == the root of the group being linked into this group
|
// 'linkPart' == the root of the group being linked into this group
|
||||||
SceneObjectPart linkPart = objectGroup.m_rootPart;
|
SceneObjectPart linkPart = objectGroup.m_rootPart;
|
||||||
|
|
||||||
|
@ -3160,7 +3180,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
axPos *= Quaternion.Conjugate(parentRot);
|
axPos *= Quaternion.Conjugate(parentRot);
|
||||||
linkPart.OffsetPosition = axPos;
|
linkPart.OffsetPosition = axPos;
|
||||||
|
|
||||||
|
|
||||||
// If there is only one SOP in a SOG, the LinkNum is zero. I.e., not a linkset.
|
// If there is only one SOP in a SOG, the LinkNum is zero. I.e., not a linkset.
|
||||||
// Now that we know this SOG has at least two SOPs in it, the new root
|
// Now that we know this SOG has at least two SOPs in it, the new root
|
||||||
// SOP becomes the first in the linkset.
|
// SOP becomes the first in the linkset.
|
||||||
|
@ -3193,8 +3212,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
linkPart.CreateSelected = true;
|
linkPart.CreateSelected = true;
|
||||||
|
|
||||||
// let physics know preserve part volume dtc messy since UpdatePrimFlags doesn't look to parent changes for now
|
linkPart.UpdatePrimFlags(grpusephys, grptemporary, (IsPhantom || (linkPart.Flags & PrimFlags.Phantom) != 0), linkPart.VolumeDetectActive || RootPart.VolumeDetectActive, true);
|
||||||
linkPart.UpdatePrimFlags(grpusephys, grptemporary, (IsPhantom || (linkPart.Flags & PrimFlags.Phantom) != 0), linkPart.VolumeDetectActive, true);
|
|
||||||
|
|
||||||
// If the added SOP is physical, also tell the physics engine about the link relationship.
|
// If the added SOP is physical, also tell the physics engine about the link relationship.
|
||||||
if (linkPart.PhysActor != null && m_rootPart.PhysActor != null && m_rootPart.PhysActor.IsPhysical)
|
if (linkPart.PhysActor != null && m_rootPart.PhysActor != null && m_rootPart.PhysActor.IsPhysical)
|
||||||
|
|
|
@ -1179,9 +1179,10 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
string old = m_mediaUrl;
|
||||||
m_mediaUrl = value;
|
m_mediaUrl = value;
|
||||||
|
|
||||||
if (ParentGroup != null)
|
if (ParentGroup != null && old != m_mediaUrl)
|
||||||
ParentGroup.HasGroupChanged = true;
|
ParentGroup.HasGroupChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1385,13 +1386,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[XmlIgnore]
|
|
||||||
public bool IsOccupied // KF If an av is sittingon this prim
|
|
||||||
{
|
|
||||||
get { return m_occupied; }
|
|
||||||
set { m_occupied = value; }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ID of the avatar that is sat on us if we have a sit target. If there is no such avatar then is UUID.Zero
|
/// ID of the avatar that is sat on us if we have a sit target. If there is no such avatar then is UUID.Zero
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -2472,12 +2466,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
public uint GetEffectiveObjectFlags()
|
public uint GetEffectiveObjectFlags()
|
||||||
{
|
{
|
||||||
// Commenting this section of code out since it doesn't actually do anything, as enums are handled by
|
|
||||||
// value rather than reference
|
|
||||||
// PrimFlags f = _flags;
|
|
||||||
// if (m_parentGroup == null || m_parentGroup.RootPart == this)
|
|
||||||
// f &= ~(PrimFlags.Touch | PrimFlags.Money);
|
|
||||||
|
|
||||||
uint eff = (uint)Flags | (uint)LocalFlags;
|
uint eff = (uint)Flags | (uint)LocalFlags;
|
||||||
if(m_inventory == null || m_inventory.Count == 0)
|
if(m_inventory == null || m_inventory.Count == 0)
|
||||||
eff |= (uint)PrimFlags.InventoryEmpty;
|
eff |= (uint)PrimFlags.InventoryEmpty;
|
||||||
|
|
|
@ -1212,7 +1212,9 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
ControllingClient.OnForceReleaseControls += HandleForceReleaseControls;
|
ControllingClient.OnForceReleaseControls += HandleForceReleaseControls;
|
||||||
ControllingClient.OnAutoPilotGo += MoveToTargetHandle;
|
ControllingClient.OnAutoPilotGo += MoveToTargetHandle;
|
||||||
ControllingClient.OnUpdateThrottles += RaiseUpdateThrottles;
|
ControllingClient.OnUpdateThrottles += RaiseUpdateThrottles;
|
||||||
// ControllingClient.OnAgentFOV += HandleAgentFOV;
|
ControllingClient.OnRegionHandShakeReply += RegionHandShakeReply;
|
||||||
|
|
||||||
|
// ControllingClient.OnAgentFOV += HandleAgentFOV;
|
||||||
|
|
||||||
// ControllingClient.OnChildAgentStatus += new StatusChange(this.ChildStatusChange);
|
// ControllingClient.OnChildAgentStatus += new StatusChange(this.ChildStatusChange);
|
||||||
// ControllingClient.OnStopMovement += new GenericCall2(this.StopMovement);
|
// ControllingClient.OnStopMovement += new GenericCall2(this.StopMovement);
|
||||||
|
@ -1232,7 +1234,9 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
ControllingClient.OnForceReleaseControls -= HandleForceReleaseControls;
|
ControllingClient.OnForceReleaseControls -= HandleForceReleaseControls;
|
||||||
ControllingClient.OnAutoPilotGo -= MoveToTargetHandle;
|
ControllingClient.OnAutoPilotGo -= MoveToTargetHandle;
|
||||||
ControllingClient.OnUpdateThrottles -= RaiseUpdateThrottles;
|
ControllingClient.OnUpdateThrottles -= RaiseUpdateThrottles;
|
||||||
// ControllingClient.OnAgentFOV += HandleAgentFOV;
|
ControllingClient.OnRegionHandShakeReply -= RegionHandShakeReply;
|
||||||
|
|
||||||
|
// ControllingClient.OnAgentFOV += HandleAgentFOV;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetDirectionVectors()
|
private void SetDirectionVectors()
|
||||||
|
@ -2126,10 +2130,15 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(IsChildAgent)
|
||||||
|
{
|
||||||
|
return; // how?
|
||||||
|
}
|
||||||
//m_log.DebugFormat("[CompleteMovement] MakeRootAgent: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
//m_log.DebugFormat("[CompleteMovement] MakeRootAgent: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
||||||
|
|
||||||
if(!haveGroupInformation && !IsChildAgent && !IsNPC)
|
if (!IsNPC)
|
||||||
|
{
|
||||||
|
if (!haveGroupInformation && !IsNPC)
|
||||||
{
|
{
|
||||||
IGroupsModule gm = m_scene.RequestModuleInterface<IGroupsModule>();
|
IGroupsModule gm = m_scene.RequestModuleInterface<IGroupsModule>();
|
||||||
if (gm != null)
|
if (gm != null)
|
||||||
|
@ -2168,14 +2177,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
m_callbackURI = null;
|
m_callbackURI = null;
|
||||||
//m_log.DebugFormat("[CompleteMovement] ReleaseAgent: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
//m_log.DebugFormat("[CompleteMovement] ReleaseAgent: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
||||||
}
|
}
|
||||||
// else
|
}
|
||||||
// {
|
|
||||||
// m_log.DebugFormat(
|
|
||||||
// "[SCENE PRESENCE]: No callback provided on CompleteMovement of {0} {1} to {2}",
|
|
||||||
// client.Name, client.AgentId, m_scene.RegionInfo.RegionName);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// Tell the client that we're totally ready
|
// Tell the client that we're totally ready
|
||||||
ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look);
|
ControllingClient.MoveAgentIntoRegion(m_scene.RegionInfo, AbsolutePosition, look);
|
||||||
//m_log.DebugFormat("[CompleteMovement] MoveAgentIntoRegion: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
//m_log.DebugFormat("[CompleteMovement] MoveAgentIntoRegion: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
||||||
|
@ -2187,8 +2189,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
int delayctnr = Util.EnvironmentTickCount();
|
int delayctnr = Util.EnvironmentTickCount();
|
||||||
|
|
||||||
if (!IsChildAgent)
|
|
||||||
{
|
|
||||||
if( ParentPart != null && !IsNPC && (crossingFlags & 0x08) != 0)
|
if( ParentPart != null && !IsNPC && (crossingFlags & 0x08) != 0)
|
||||||
{
|
{
|
||||||
ParentPart.ParentGroup.SendFullAnimUpdateToClient(ControllingClient);
|
ParentPart.ParentGroup.SendFullAnimUpdateToClient(ControllingClient);
|
||||||
|
@ -2212,7 +2212,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//m_log.DebugFormat("[CompleteMovement] Baked check: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
//m_log.DebugFormat("[CompleteMovement] Baked check: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
||||||
}
|
|
||||||
|
|
||||||
if(m_teleportFlags > 0)
|
if(m_teleportFlags > 0)
|
||||||
{
|
{
|
||||||
|
@ -2251,8 +2250,6 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
landch.sendClientInitialLandInfo(client, !gotCrossUpdate);
|
landch.sendClientInitialLandInfo(client, !gotCrossUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!IsChildAgent)
|
|
||||||
{
|
|
||||||
List<ScenePresence> allpresences = m_scene.GetScenePresences();
|
List<ScenePresence> allpresences = m_scene.GetScenePresences();
|
||||||
|
|
||||||
// send avatar object to all presences including us, so they cross it into region
|
// send avatar object to all presences including us, so they cross it into region
|
||||||
|
@ -2348,7 +2345,8 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(!IsNPC)
|
||||||
|
{
|
||||||
//m_log.DebugFormat("[CompleteMovement] attachments: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
//m_log.DebugFormat("[CompleteMovement] attachments: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
||||||
if (openChildAgents)
|
if (openChildAgents)
|
||||||
{
|
{
|
||||||
|
@ -2366,23 +2364,22 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
|
|
||||||
m_lastChildAgentUpdateGodLevel = GodController.ViwerUIGodLevel;
|
m_lastChildAgentUpdateGodLevel = GodController.ViwerUIGodLevel;
|
||||||
m_childUpdatesBusy = false; // allow them
|
m_childUpdatesBusy = false; // allow them
|
||||||
}
|
|
||||||
|
|
||||||
//m_log.DebugFormat("[CompleteMovement] openChildAgents: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
//m_log.DebugFormat("[CompleteMovement] openChildAgents: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
||||||
|
|
||||||
// send the rest of the world
|
// send the rest of the world
|
||||||
if (m_teleportFlags > 0 && !IsNPC || m_currentParcelHide)
|
if (m_teleportFlags > 0 | m_currentParcelHide)
|
||||||
SendInitialDataToMe();
|
SendInitialDataToMe();
|
||||||
|
|
||||||
// priority uses avatar position only
|
// priority uses avatar position only
|
||||||
// m_reprioritizationLastPosition = AbsolutePosition;
|
// m_reprioritizationLastPosition = AbsolutePosition;
|
||||||
// m_reprioritizationLastDrawDistance = DrawDistance;
|
// m_reprioritizationLastDrawDistance = DrawDistance;
|
||||||
// m_reprioritizationLastTime = Util.EnvironmentTickCount() + 15000; // delay it
|
// m_reprioritizationLastTime = Util.EnvironmentTickCount() + 15000; // delay it
|
||||||
// m_reprioritizationBusy = false;
|
// m_reprioritizationBusy = false;
|
||||||
|
|
||||||
//m_log.DebugFormat("[CompleteMovement] SendInitialDataToMe: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
//m_log.DebugFormat("[CompleteMovement] SendInitialDataToMe: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
||||||
|
|
||||||
if (!IsChildAgent && openChildAgents)
|
if (openChildAgents)
|
||||||
{
|
{
|
||||||
IFriendsModule friendsModule = m_scene.RequestModuleInterface<IFriendsModule>();
|
IFriendsModule friendsModule = m_scene.RequestModuleInterface<IFriendsModule>();
|
||||||
if (friendsModule != null)
|
if (friendsModule != null)
|
||||||
|
@ -2393,7 +2390,7 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
friendsModule.SendFriendsOnlineIfNeeded(ControllingClient);
|
friendsModule.SendFriendsOnlineIfNeeded(ControllingClient);
|
||||||
}
|
}
|
||||||
//m_log.DebugFormat("[CompleteMovement] friendsModule: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
//m_log.DebugFormat("[CompleteMovement] friendsModule: {0}ms", Util.EnvironmentTickCountSubtract(ts));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -4024,10 +4021,100 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
ControllingClient.SendCoarseLocationUpdate(avatarUUIDs, coarseLocations);
|
ControllingClient.SendCoarseLocationUpdate(avatarUUIDs, coarseLocations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void RegionHandShakeReply (IClientAPI client, uint flags)
|
||||||
|
{
|
||||||
|
if(IsNPC)
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool selfappearance = (flags & 4) != 0;
|
||||||
|
bool cacheCulling = (flags & 1) != 0;
|
||||||
|
bool cacheEmpty;
|
||||||
|
if(cacheCulling)
|
||||||
|
cacheEmpty = (flags & 2) != 0;
|
||||||
|
else
|
||||||
|
cacheEmpty = true;
|
||||||
|
|
||||||
|
if (m_teleportFlags > 0) // only doing for child for now
|
||||||
|
return;
|
||||||
|
|
||||||
|
lock (m_completeMovementLock)
|
||||||
|
{
|
||||||
|
if (SentInitialData)
|
||||||
|
return;
|
||||||
|
SentInitialData = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.FireAndForget(delegate
|
||||||
|
{
|
||||||
|
Scene.SendLayerData(ControllingClient);
|
||||||
|
|
||||||
|
ILandChannel landch = m_scene.LandChannel;
|
||||||
|
if (landch != null)
|
||||||
|
landch.sendClientInitialLandInfo(ControllingClient, true);
|
||||||
|
|
||||||
|
// recheck to reduce timing issues
|
||||||
|
ControllingClient.CheckViewerCaps();
|
||||||
|
|
||||||
|
SendOtherAgentsAvatarFullToMe();
|
||||||
|
/*
|
||||||
|
if (m_scene.ObjectsCullingByDistance && cacheCulling)
|
||||||
|
{
|
||||||
|
m_reprioritizationBusy = true;
|
||||||
|
m_reprioritizationLastPosition = AbsolutePosition;
|
||||||
|
m_reprioritizationLastDrawDistance = DrawDistance;
|
||||||
|
|
||||||
|
ControllingClient.ReprioritizeUpdates();
|
||||||
|
m_reprioritizationLastTime = Util.EnvironmentTickCount();
|
||||||
|
m_reprioritizationBusy = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
EntityBase[] entities = Scene.Entities.GetEntities();
|
||||||
|
if(cacheEmpty)
|
||||||
|
{
|
||||||
|
foreach (EntityBase e in entities)
|
||||||
|
{
|
||||||
|
if (e != null && e is SceneObjectGroup && !((SceneObjectGroup)e).IsAttachment)
|
||||||
|
((SceneObjectGroup)e).SendFullAnimUpdateToClient(ControllingClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foreach (EntityBase e in entities)
|
||||||
|
{
|
||||||
|
if (e != null && e is SceneObjectGroup && !((SceneObjectGroup)e).IsAttachment)
|
||||||
|
{
|
||||||
|
SceneObjectGroup grp = e as SceneObjectGroup;
|
||||||
|
if(grp.IsViewerCachable)
|
||||||
|
grp.SendUpdateProbes(ControllingClient);
|
||||||
|
else
|
||||||
|
grp.SendFullAnimUpdateToClient(ControllingClient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_reprioritizationLastPosition = AbsolutePosition;
|
||||||
|
m_reprioritizationLastDrawDistance = DrawDistance;
|
||||||
|
m_reprioritizationLastTime = Util.EnvironmentTickCount() + 15000; // delay it
|
||||||
|
|
||||||
|
m_reprioritizationBusy = false;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void SendInitialDataToMe()
|
public void SendInitialDataToMe()
|
||||||
{
|
{
|
||||||
// Send all scene object to the new client
|
// Send all scene object to the new client
|
||||||
|
lock (m_completeMovementLock)
|
||||||
|
{
|
||||||
|
if (SentInitialData)
|
||||||
|
return;
|
||||||
SentInitialData = true;
|
SentInitialData = true;
|
||||||
|
}
|
||||||
|
|
||||||
Util.FireAndForget(delegate
|
Util.FireAndForget(delegate
|
||||||
{
|
{
|
||||||
// we created a new ScenePresence (a new child agent) in a fresh region.
|
// we created a new ScenePresence (a new child agent) in a fresh region.
|
||||||
|
@ -4280,7 +4367,13 @@ namespace OpenSim.Region.Framework.Scenes
|
||||||
if(IsDeleted || !ControllingClient.IsActive)
|
if(IsDeleted || !ControllingClient.IsActive)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(!SentInitialData)
|
bool needsendinitial = false;
|
||||||
|
lock(m_completeMovementLock)
|
||||||
|
{
|
||||||
|
needsendinitial = SentInitialData;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!needsendinitial)
|
||||||
{
|
{
|
||||||
SendInitialDataToMe();
|
SendInitialDataToMe();
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -699,7 +699,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
|
||||||
public event TeleportCancel OnTeleportCancel;
|
public event TeleportCancel OnTeleportCancel;
|
||||||
public event DeRezObject OnDeRezObject;
|
public event DeRezObject OnDeRezObject;
|
||||||
public event RezRestoreToWorld OnRezRestoreToWorld;
|
public event RezRestoreToWorld OnRezRestoreToWorld;
|
||||||
public event Action<IClientAPI> OnRegionHandShakeReply;
|
public event Action<IClientAPI, uint> OnRegionHandShakeReply;
|
||||||
public event GenericCall1 OnRequestWearables;
|
public event GenericCall1 OnRequestWearables;
|
||||||
public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
|
public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
|
||||||
public event UpdateAgent OnPreAgentUpdate;
|
public event UpdateAgent OnPreAgentUpdate;
|
||||||
|
@ -938,7 +938,7 @@ namespace OpenSim.Region.OptionalModules.Agent.InternetRelayClientView.Server
|
||||||
|
|
||||||
if (OnRegionHandShakeReply != null)
|
if (OnRegionHandShakeReply != null)
|
||||||
{
|
{
|
||||||
OnRegionHandShakeReply(this);
|
OnRegionHandShakeReply(this, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (OnCompleteMovementToRegion != null)
|
if (OnCompleteMovementToRegion != null)
|
||||||
|
|
|
@ -319,7 +319,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
|
||||||
|
|
||||||
public event DeRezObject OnDeRezObject;
|
public event DeRezObject OnDeRezObject;
|
||||||
public event RezRestoreToWorld OnRezRestoreToWorld;
|
public event RezRestoreToWorld OnRezRestoreToWorld;
|
||||||
public event Action<IClientAPI> OnRegionHandShakeReply;
|
public event Action<IClientAPI, uint> OnRegionHandShakeReply;
|
||||||
public event GenericCall1 OnRequestWearables;
|
public event GenericCall1 OnRequestWearables;
|
||||||
public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
|
public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
|
||||||
public event UpdateAgent OnPreAgentUpdate;
|
public event UpdateAgent OnPreAgentUpdate;
|
||||||
|
@ -928,7 +928,7 @@ namespace OpenSim.Region.OptionalModules.World.NPC
|
||||||
{
|
{
|
||||||
if (OnRegionHandShakeReply != null)
|
if (OnRegionHandShakeReply != null)
|
||||||
{
|
{
|
||||||
OnRegionHandShakeReply(this);
|
OnRegionHandShakeReply(this, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -119,7 +119,7 @@ namespace OpenSim.Tests.Common
|
||||||
|
|
||||||
public event DeRezObject OnDeRezObject;
|
public event DeRezObject OnDeRezObject;
|
||||||
public event RezRestoreToWorld OnRezRestoreToWorld;
|
public event RezRestoreToWorld OnRezRestoreToWorld;
|
||||||
public event Action<IClientAPI> OnRegionHandShakeReply;
|
public event Action<IClientAPI, uint> OnRegionHandShakeReply;
|
||||||
public event GenericCall1 OnRequestWearables;
|
public event GenericCall1 OnRequestWearables;
|
||||||
public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
|
public event Action<IClientAPI, bool> OnCompleteMovementToRegion;
|
||||||
public event UpdateAgent OnPreAgentUpdate;
|
public event UpdateAgent OnPreAgentUpdate;
|
||||||
|
@ -880,7 +880,7 @@ namespace OpenSim.Tests.Common
|
||||||
{
|
{
|
||||||
if (OnRegionHandShakeReply != null)
|
if (OnRegionHandShakeReply != null)
|
||||||
{
|
{
|
||||||
OnRegionHandShakeReply(this);
|
OnRegionHandShakeReply(this, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue