From f58e1f626562778a5491a6bad79b18b3962a6c38 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 15 Feb 2019 01:09:37 +0000 Subject: [PATCH 01/62] mantis 8479: deep change DeRezObjects(..) doing independent permitions checks per action. m_useTrashOnDelete should now work except if god deletes, but still not recomended --- .../Scenes/AsyncSceneObjectGroupDeleter.cs | 12 +- .../Framework/Scenes/Scene.Inventory.cs | 176 ++++++++---------- 2 files changed, 78 insertions(+), 110 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs b/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs index 7509686bbb..eae6d6f44c 100644 --- a/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs +++ b/OpenSim/Region/Framework/Scenes/AsyncSceneObjectGroupDeleter.cs @@ -94,19 +94,15 @@ namespace OpenSim.Region.Framework.Scenes m_inventoryDeletes.Enqueue(dtis); } - if (Enabled) - lock (m_inventoryTicker) - m_inventoryTicker.Start(); - - // Visually remove it, even if it isnt really gone yet. This means that if we crash before the object - // has gone to inventory, it will reappear in the region again on restart instead of being lost. - // This is not ideal since the object will still be available for manipulation when it should be, but it's - // better than losing the object for now. if (permissionToDelete) { foreach (SceneObjectGroup g in objectGroups) g.DeleteGroupFromScene(false); } + + if (Enabled) + lock (m_inventoryTicker) + m_inventoryTicker.Start(); } private void InventoryRunDeleteTimer(object sender, ElapsedEventArgs e) diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index e6e035402f..6450c8bf8c 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -2110,7 +2110,7 @@ namespace OpenSim.Region.Framework.Scenes // build a list of eligible objects List deleteIDs = new List(); List deleteGroups = new List(); - List takeGroups = new List(); + List takeCopyGroups = new List(); List takeDeleteGroups = new List(); ScenePresence sp = null; @@ -2119,11 +2119,10 @@ namespace OpenSim.Region.Framework.Scenes else if(action != DeRezAction.Return) return; // only Return can be called without a client - // Start with true for both, then remove the flags if objects - // that we can't derez are part of the selection - bool permissionToTake = true; - bool permissionToTakeCopy = true; - bool permissionToDelete = true; + // this is not as 0.8x code + // 0.8x did refuse all operation is not allowed on all objects + // this will do it on allowed objects + // current viewers only ask if all allowed foreach (uint localID in localIDs) { @@ -2136,8 +2135,8 @@ namespace OpenSim.Region.Framework.Scenes continue; } - // Already deleted by someone else - if (part.ParentGroup.IsDeleted) + SceneObjectGroup grp = part.ParentGroup; + if (grp == null || grp.IsDeleted) { //Client still thinks the object exists, kill it deleteIDs.Add(localID); @@ -2145,128 +2144,101 @@ namespace OpenSim.Region.Framework.Scenes } // Can't delete child prims - if (part != part.ParentGroup.RootPart) + if (part != grp.RootPart) continue; - SceneObjectGroup grp = part.ParentGroup; if (grp.IsAttachment) - continue; + { + if(!sp.IsGod || action != DeRezAction.Return || action != DeRezAction.Delete) + continue; + // this may break the attachment, but its a security action + // viewers don't allow it anyways + } // If child prims have invalid perms, fix them grp.AdjustChildPrimPermissions(false); - if (remoteClient == null) + switch (action) { - // Autoreturn has a null client. Nothing else does. So - // allow only returns - if (action != DeRezAction.Return) + case DeRezAction.SaveToExistingUserInventoryItem: { - m_log.WarnFormat( - "[AGENT INVENTORY]: Ignoring attempt to {0} {1} {2} without a client", - action, grp.Name, grp.UUID); - return; + if (Permissions.CanTakeCopyObject(grp, sp)) + takeCopyGroups.Add(grp); + break; } - permissionToTakeCopy = false; - } - else - { - if (action == DeRezAction.TakeCopy) + case DeRezAction.TakeCopy: { - if (!Permissions.CanTakeCopyObject(grp, sp)) - permissionToTakeCopy = false; - } - else - { - permissionToTakeCopy = false; - } - if (!Permissions.CanTakeObject(grp, sp)) - permissionToTake = false; - - if (!Permissions.CanDeleteObject(grp, remoteClient)) - permissionToDelete = false; - } - - // Handle god perms - if ((remoteClient != null) && Permissions.IsGod(remoteClient.AgentId)) - { - permissionToTake = true; - permissionToTakeCopy = true; - permissionToDelete = true; - } - - // If we're re-saving, we don't even want to delete - if (action == DeRezAction.SaveToExistingUserInventoryItem) - permissionToDelete = false; - - // if we want to take a copy, we also don't want to delete - // Note: after this point, the permissionToTakeCopy flag - // becomes irrelevant. It already includes the permissionToTake - // permission and after excluding no copy items here, we can - // just use that. - if (action == DeRezAction.TakeCopy) - { - // If we don't have permission, stop right here - if (!permissionToTakeCopy) - { - remoteClient.SendAlertMessage("You don't have permission to take the object"); - return; + if (Permissions.CanTakeCopyObject(grp, sp)) + takeCopyGroups.Add(grp); + break; } - permissionToTake = true; - // Don't delete - permissionToDelete = false; - } - - if (action == DeRezAction.Return) - { - if (remoteClient != null) + case DeRezAction.Take: { - if (Permissions.CanReturnObjects( - null, - remoteClient, - new List() {grp})) + if (Permissions.CanTakeObject(grp, sp)) + takeDeleteGroups.Add(grp); + break; + } + + case DeRezAction.GodTakeCopy: + { + if((remoteClient != null) && Permissions.IsGod(remoteClient.AgentId)) + takeCopyGroups.Add(grp); + break; + } + + case DeRezAction.Delete: + { + if (Permissions.CanDeleteObject(grp, remoteClient)) { - permissionToTake = true; - permissionToDelete = true; - if(AddToReturns) - AddReturn(grp.OwnerID == grp.GroupID ? grp.LastOwnerID : grp.OwnerID, grp.Name, grp.AbsolutePosition, - "parcel owner return"); + if(m_useTrashOnDelete || (sp.IsGod && grp.OwnerID != sp.UUID)) + takeDeleteGroups.Add(grp); + else + deleteGroups.Add(grp); } + break; } - else // Auto return passes through here with null agent - { - permissionToTake = true; - permissionToDelete = true; - } - } - if (permissionToDelete) - { - if (permissionToTake) - takeDeleteGroups.Add(grp); - else - deleteGroups.Add(grp); - deleteIDs.Add(grp.LocalId); + case DeRezAction.Return: + { + if (remoteClient != null) + { + if (Permissions.CanReturnObjects( null, remoteClient, new List() {grp})) + { + takeDeleteGroups.Add(grp); + if (AddToReturns) + AddReturn(grp.OwnerID == grp.GroupID ? grp.LastOwnerID : grp.OwnerID, grp.Name, grp.AbsolutePosition, + "parcel owner return"); + } + } + else // Auto return passes through here with null agent + { + takeDeleteGroups.Add(grp); + } + break; + } + + default: + break; } - else if(permissionToTake) - takeGroups.Add(grp); } - SendKillObject(deleteIDs); + if(deleteIDs.Count > 0) + SendKillObject(deleteIDs); if (takeDeleteGroups.Count > 0) { - m_asyncSceneObjectDeleter.DeleteToInventory( - action, destinationID, takeDeleteGroups, remoteClient, - true); + m_asyncSceneObjectDeleter.DeleteToInventory(action, destinationID, takeDeleteGroups, + remoteClient, true); } - if (takeGroups.Count > 0) + + if (takeCopyGroups.Count > 0) { - m_asyncSceneObjectDeleter.DeleteToInventory( - action, destinationID, takeGroups, remoteClient, - false); + m_asyncSceneObjectDeleter.DeleteToInventory(action, destinationID, takeCopyGroups, + remoteClient, false); } + if (deleteGroups.Count > 0) { foreach (SceneObjectGroup g in deleteGroups) From 06930a180360e4ec54d8bf44c80794edb0a5a17c Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 15 Feb 2019 01:10:56 +0000 Subject: [PATCH 02/62] don't break permitions on god object return or delete --- .../InventoryAccess/InventoryAccessModule.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs index d6c65a1e3f..69c1e4edce 100644 --- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs +++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs @@ -526,16 +526,23 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess item.AssetType = (int)AssetType.Object; item.AssetID = asset.FullID; - if (DeRezAction.SaveToExistingUserInventoryItem == action) + if (action == DeRezAction.SaveToExistingUserInventoryItem) { m_Scene.InventoryService.UpdateItem(item); } else { - AddPermissions(item, objlist[0], objlist, remoteClient); + bool isowner = remoteClient != null && item.Owner == remoteClient.AgentId; + if(action == DeRezAction.Return) + AddPermissions(item, objlist[0], objlist, null); + else if(action == DeRezAction.Delete && !isowner) + AddPermissions(item, objlist[0], objlist, null); + else + AddPermissions(item, objlist[0], objlist, remoteClient); + m_Scene.AddInventoryItem(item); - if (remoteClient != null && item.Owner == remoteClient.AgentId) + if (isowner) { remoteClient.SendInventoryItemCreateUpdate(item, 0); } @@ -1010,7 +1017,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess group.CreateScriptInstances(0, true, m_Scene.DefaultScriptEngine, 1); rootPart.ParentGroup.ResumeScripts(); - group.ScheduleGroupForFullUpdate(); + group.ScheduleGroupForFullAnimUpdate(); } else m_Scene.AddNewSceneObject(group, true, false); From b242232c7b1cfd682fa92c8616c0e299539bd4b0 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 15 Feb 2019 01:38:49 +0000 Subject: [PATCH 03/62] minor cleanup --- OpenSim/Framework/TerrainData.cs | 1 - OpenSim/Framework/Util.cs | 28 ---------------------------- 2 files changed, 29 deletions(-) diff --git a/OpenSim/Framework/TerrainData.cs b/OpenSim/Framework/TerrainData.cs index 5604628d95..1a2d5d1e5f 100644 --- a/OpenSim/Framework/TerrainData.cs +++ b/OpenSim/Framework/TerrainData.cs @@ -212,7 +212,6 @@ namespace OpenSim.Framework return heights; } - // TerrainData.GetDoubles public double[,] GetDoubles() { double[,] ret = new double[SizeX, SizeY]; diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs index 52f9aeab2b..f66a98736d 100644 --- a/OpenSim/Framework/Util.cs +++ b/OpenSim/Framework/Util.cs @@ -2437,34 +2437,6 @@ namespace OpenSim.Framework #region FireAndForget Threading Pattern - /// - /// Created to work around a limitation in Mono with nested delegates - /// - private sealed class FireAndForgetWrapper - { - private static object syncRoot = new Object(); - - public void FireAndForget(System.Threading.WaitCallback callback) - { - callback.BeginInvoke(null, EndFireAndForget, callback); - } - - public void FireAndForget(System.Threading.WaitCallback callback, object obj) - { - callback.BeginInvoke(obj, EndFireAndForget, callback); - } - - private static void EndFireAndForget(IAsyncResult ar) - { - System.Threading.WaitCallback callback = (System.Threading.WaitCallback)ar.AsyncState; - - try { callback.EndInvoke(ar); } - catch (Exception ex) { m_log.Error("[UTIL]: Asynchronous method threw an exception: " + ex.Message, ex); } - - ar.AsyncWaitHandle.Close(); - } - } - public static void InitThreadPool(int minThreads, int maxThreads) { if (maxThreads < 2) From 040ab65f680583d4c73e9edb8004b60e1fe25e86 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 15 Feb 2019 02:08:45 +0000 Subject: [PATCH 04/62] (almost) useless change --- OpenSim/Framework/ClientManager.cs | 65 +++++++++++++++++++----------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/OpenSim/Framework/ClientManager.cs b/OpenSim/Framework/ClientManager.cs index 45c54e4989..1508c7f505 100644 --- a/OpenSim/Framework/ClientManager.cs +++ b/OpenSim/Framework/ClientManager.cs @@ -51,7 +51,14 @@ namespace OpenSim.Framework private object m_syncRoot = new object(); /// Number of clients in the collection - public int Count { get { return m_dict1.Count; } } + public int Count + { + get + { + lock (m_syncRoot) + return m_dict1.Count; + } + } /// /// Default constructor @@ -60,7 +67,7 @@ namespace OpenSim.Framework { m_dict1 = new Dictionary(); m_dict2 = new Dictionary(); - m_array = new IClientAPI[0]; + m_array = null; } /// @@ -74,17 +81,9 @@ namespace OpenSim.Framework { lock (m_syncRoot) { - // allow self healing -// if (m_dict1.ContainsKey(value.AgentId) || m_dict2.ContainsKey(value.RemoteEndPoint)) -// return false; - m_dict1[value.AgentId] = value; m_dict2[value.RemoteEndPoint] = value; - - // dict1 is the master - IClientAPI[] newArray = new IClientAPI[m_dict1.Count]; - m_dict1.Values.CopyTo(newArray, 0); - m_array = newArray; + m_array = null; } return true; @@ -105,10 +104,7 @@ namespace OpenSim.Framework { m_dict1.Remove(key); m_dict2.Remove(value.RemoteEndPoint); - - IClientAPI[] newArray = new IClientAPI[m_dict1.Count]; - m_dict1.Values.CopyTo(newArray, 0); - m_array = newArray; + m_array = null; return true; } } @@ -124,7 +120,7 @@ namespace OpenSim.Framework { m_dict1.Clear(); m_dict2.Clear(); - m_array = new IClientAPI[0]; + m_array = null; } } @@ -135,7 +131,8 @@ namespace OpenSim.Framework /// True if the UUID was found in the collection, otherwise false public bool ContainsKey(UUID key) { - return m_dict1.ContainsKey(key); + lock (m_syncRoot) + return m_dict1.ContainsKey(key); } /// @@ -145,7 +142,8 @@ namespace OpenSim.Framework /// True if the endpoint was found in the collection, otherwise false public bool ContainsKey(IPEndPoint key) { - return m_dict2.ContainsKey(key); + lock (m_syncRoot) + return m_dict2.ContainsKey(key); } /// @@ -156,8 +154,12 @@ namespace OpenSim.Framework /// True if the lookup succeeded, otherwise false public bool TryGetValue(UUID key, out IClientAPI value) { - try { return m_dict1.TryGetValue(key, out value); } - catch (Exception) + try + { + lock (m_syncRoot) + return m_dict1.TryGetValue(key, out value); + } + catch { value = null; return false; @@ -172,8 +174,12 @@ namespace OpenSim.Framework /// True if the lookup succeeded, otherwise false public bool TryGetValue(IPEndPoint key, out IClientAPI value) { - try { return m_dict2.TryGetValue(key, out value); } - catch (Exception) + try + { + lock (m_syncRoot) + return m_dict2.TryGetValue(key, out value); + } + catch { value = null; return false; @@ -187,7 +193,20 @@ namespace OpenSim.Framework /// Action to perform on each element public void ForEach(Action action) { - IClientAPI[] localArray = m_array; + IClientAPI[] localArray; + lock (m_syncRoot) + { + if (m_array == null) + { + if (m_dict1.Count == 0) + return; + + m_array = new IClientAPI[m_dict1.Count]; + m_dict1.Values.CopyTo(m_array, 0); + } + localArray = m_array; + } + for (int i = 0; i < localArray.Length; i++) action(localArray[i]); } From 98b6ba24b4340f0ed2f14d379952d3f5886f4282 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 15 Feb 2019 02:28:00 +0000 Subject: [PATCH 05/62] oops do show the object delete --- OpenSim/Region/Framework/Scenes/Scene.Inventory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs index 6450c8bf8c..debcad373d 100644 --- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs @@ -2242,7 +2242,7 @@ namespace OpenSim.Region.Framework.Scenes if (deleteGroups.Count > 0) { foreach (SceneObjectGroup g in deleteGroups) - DeleteSceneObject(g, true); + DeleteSceneObject(g, false); } } From fdf5274c257286d6e438a337e61761028ff8003a Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 20 Feb 2019 02:06:25 +0000 Subject: [PATCH 06/62] add osKickAvatar(LSL_Key agentKey, string alert) --- .../Shared/Api/Implementation/OSSL_Api.cs | 41 +++++++++++++++---- .../Shared/Api/Interface/IOSSL_Api.cs | 15 +++---- .../Shared/Api/Runtime/OSSL_Stub.cs | 19 +++++---- 3 files changed, 53 insertions(+), 22 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 9d5f67014c..e114a53455 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -3629,7 +3629,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { CheckThreatLevel(ThreatLevel.Severe, "osKickAvatar"); - World.ForEachRootScenePresence(delegate(ScenePresence sp) + World.ForEachRootScenePresence(delegate (ScenePresence sp) { if (sp.Firstname == FirstName && sp.Lastname == SurName) { @@ -3643,18 +3643,43 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api }); } - public LSL_Float osGetHealth(string avatar) + public void osKickAvatar(LSL_Key agentKey, string alert) + { + CheckThreatLevel(ThreatLevel.Severe, "osKickAvatar"); + + UUID id; + if (!UUID.TryParse(agentKey, out id) || id == UUID.Zero) + return; + + ScenePresence sp = World.GetScenePresence(id); + if(sp == null) + return; + + // kick client... + if (alert != null) + sp.ControllingClient.Kick(alert); + + // ...and close on our side + sp.Scene.CloseAgent(id, false); + } + + public LSL_Float osGetHealth(LSL_Key agentKey) { CheckThreatLevel(ThreatLevel.None, "osGetHealth"); LSL_Float health = new LSL_Float(-1); - ScenePresence presence = World.GetScenePresence(new UUID(avatar)); + + UUID id; + if (!UUID.TryParse(agentKey, out id) || id == UUID.Zero) + return health; + + ScenePresence presence = World.GetScenePresence(id); if (presence != null) health = presence.Health; return health; } - public void osCauseDamage(string avatar, double damage) + public void osCauseDamage(LSL_Key avatar, LSL_Float damage) { CheckThreatLevel(ThreatLevel.High, "osCauseDamage"); @@ -3683,7 +3708,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } - public void osCauseHealing(string avatar, double healing) + public void osCauseHealing(LSL_Key avatar, LSL_Float healing) { CheckThreatLevel(ThreatLevel.High, "osCauseHealing"); @@ -3704,7 +3729,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api presence.setHealthWithUpdate(health); } - public void osSetHealth(string avatar, double health) + public void osSetHealth(LSL_Key avatar, LSL_Float health) { CheckThreatLevel(ThreatLevel.High, "osSetHealth"); @@ -3722,7 +3747,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } - public void osSetHealRate(string avatar, double healrate) + public void osSetHealRate(LSL_Key avatar, LSL_Float healrate) { CheckThreatLevel(ThreatLevel.High, "osSetHealRate"); @@ -3737,7 +3762,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api presence.HealRate = (float)healrate; } - public LSL_Float osGetHealRate(string avatar) + public LSL_Float osGetHealRate(LSL_Key avatar) { CheckThreatLevel(ThreatLevel.None, "osGetHealRate"); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs index 12e8103b3d..67c0261625 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -379,15 +379,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces int osGetSimulatorMemory(); int osGetSimulatorMemoryKB(); - void osKickAvatar(string FirstName,string SurName,string alert); + void osKickAvatar(string FirstName, string SurName, string alert); + void osKickAvatar(LSL_Key agentId, string alert); void osSetSpeed(string UUID, LSL_Float SpeedModifier); void osSetOwnerSpeed(LSL_Float SpeedModifier); - LSL_Float osGetHealth(string avatar); - void osCauseHealing(string avatar, double healing); - void osSetHealth(string avatar, double health); - void osSetHealRate(string avatar, double health); - LSL_Float osGetHealRate(string avatar); - void osCauseDamage(string avatar, double damage); + LSL_Float osGetHealth(key agentId); + void osCauseHealing(key agentId, LSL_Float healing); + void osSetHealth(key agentId, LSL_Float health); + void osSetHealRate(key agentId, LSL_Float health); + LSL_Float osGetHealRate(key agentId); + void osCauseDamage(key avatar, LSL_Float damage); void osForceOtherSit(string avatar); void osForceOtherSit(string avatar, string target); LSL_List osGetPrimitiveParams(LSL_Key prim, LSL_List rules); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index 94df1eaa76..76d334b915 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -965,11 +965,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase return m_OSSL_Functions.osGetSimulatorMemoryKB(); } - public void osKickAvatar(string FirstName,string SurName,string alert) + public void osKickAvatar(string FirstName, string SurName, string alert) { m_OSSL_Functions.osKickAvatar(FirstName, SurName, alert); } + public void osKickAvatar(LSL_Key agentId, string alert) + { + m_OSSL_Functions.osKickAvatar(agentId, alert); + } + public void osSetSpeed(string UUID, LSL_Float SpeedModifier) { m_OSSL_Functions.osSetSpeed(UUID, SpeedModifier); @@ -980,32 +985,32 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase m_OSSL_Functions.osSetOwnerSpeed(SpeedModifier); } - public LSL_Float osGetHealth(string avatar) + public LSL_Float osGetHealth(key avatar) { return m_OSSL_Functions.osGetHealth(avatar); } - public void osCauseDamage(string avatar, double damage) + public void osCauseDamage(key avatar, LSL_Float damage) { m_OSSL_Functions.osCauseDamage(avatar, damage); } - public void osCauseHealing(string avatar, double healing) + public void osCauseHealing(key avatar, LSL_Float healing) { m_OSSL_Functions.osCauseHealing(avatar, healing); } - public void osSetHealth(string avatar, double health) + public void osSetHealth(key avatar, LSL_Float health) { m_OSSL_Functions.osSetHealth(avatar, health); } - public void osSetHealRate(string avatar, double health) + public void osSetHealRate(key avatar, LSL_Float health) { m_OSSL_Functions.osSetHealRate(avatar, health); } - public LSL_Float osGetHealRate(string avatar) + public LSL_Float osGetHealRate(key avatar) { return m_OSSL_Functions.osGetHealRate(avatar); } From b56eb2fe63a3bc1aaa954a24c46a1725d5bb704d Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 20 Feb 2019 03:05:23 +0000 Subject: [PATCH 07/62] estate kick now just kill user connection if on same region. The teleport home we did, does not make much sense, and would need more work anyways --- .../ClientStack/Linden/UDP/LLClientView.cs | 13 +++------- .../World/Estate/EstateManagementModule.cs | 25 +++++++++++-------- .../CoreModules/World/Estate/EstateModule.cs | 23 ++++++++++++++--- 3 files changed, 39 insertions(+), 22 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index b7d5a80dfd..943be07d2d 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -1677,8 +1677,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP public void SendKillObject(List localIDs) { - // foreach (uint id in localIDs) - // m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, id, regionHandle); + // foreach (uint id in localIDs) + // m_log.DebugFormat("[CLIENT]: Sending KillObjectPacket to {0} for {1} in {2}", Name, id, regionHandle); // remove pending entities to reduce looping chances. lock (m_entityProps.SyncRoot) @@ -1702,10 +1702,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if(++nsent >= 200) { - kill.Header.Reliable = true; - kill.Header.Zerocoded = true; OutPacket(kill, ThrottleOutPacketType.Task); - perpacket = localIDs.Count - i - 1; if(perpacket == 0) break; @@ -1720,8 +1717,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if(nsent != 0) { - kill.Header.Reliable = true; - kill.Header.Zerocoded = true; OutPacket(kill, ThrottleOutPacketType.Task); } } @@ -10047,7 +10042,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP UUID.TryParse(Utils.BytesToString(messagePacket.ParamList[1].Parameter), out Prey); - OnEstateTeleportOneUserHomeRequest(this, invoice, SenderID, Prey); + OnEstateTeleportOneUserHomeRequest(this, invoice, SenderID, Prey, false); } return true; case "teleporthomeallusers": @@ -10195,7 +10190,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP UUID.TryParse(Utils.BytesToString(messagePacket.ParamList[0].Parameter), out Prey); - OnEstateTeleportOneUserHomeRequest(this, invoice, SenderID, Prey); + OnEstateTeleportOneUserHomeRequest(this, invoice, SenderID, Prey, true); } return true; diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs index 2e801e35b4..ac28ceeac0 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs @@ -1204,28 +1204,33 @@ namespace OpenSim.Region.CoreModules.World.Estate } } - private void handleEstateTeleportOneUserHomeRequest(IClientAPI remover_client, UUID invoice, UUID senderID, UUID prey) + private void handleEstateTeleportOneUserHomeRequest(IClientAPI remover_client, UUID invoice, UUID senderID, UUID prey, bool kick) { + if (prey == UUID.Zero) + return; + EstateTeleportOneUserHomeRequest evOverride = OnEstateTeleportOneUserHomeRequest; if(evOverride != null) { - evOverride(remover_client, invoice, senderID, prey); + evOverride(remover_client, invoice, senderID, prey, kick); return; } if (!Scene.Permissions.CanIssueEstateCommand(remover_client.AgentId, false)) return; - if (prey != UUID.Zero) + ScenePresence s = Scene.GetScenePresence(prey); + if (s != null && !s.IsDeleted && !s.IsInTransit) { - ScenePresence s = Scene.GetScenePresence(prey); - if (s != null && !s.IsDeleted && !s.IsInTransit) + if (kick) { - if (!Scene.TeleportClientHome(prey, s.ControllingClient)) - { - s.ControllingClient.Kick("You were teleported home by the region owner, but the TP failed - you have been logged out."); - Scene.CloseAgent(s.UUID, false); - } + s.ControllingClient.Kick("You have been kicked"); + Scene.CloseAgent(s.UUID, false); + } + else if (!Scene.TeleportClientHome(prey, s.ControllingClient)) + { + s.ControllingClient.Kick("You were teleported home by the region owner, but the TP failed "); + Scene.CloseAgent(s.UUID, false); } } } diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateModule.cs index f4a174a7a7..c8b9032c36 100644 --- a/OpenSim/Region/CoreModules/World/Estate/EstateModule.cs +++ b/OpenSim/Region/CoreModules/World/Estate/EstateModule.cs @@ -204,7 +204,7 @@ namespace OpenSim.Region.CoreModules.World.Estate m_EstateConnector.SendEstateMessage(estateID, FromID, FromName, Message); } - private void OnEstateTeleportOneUserHomeRequest(IClientAPI client, UUID invoice, UUID senderID, UUID prey) + private void OnEstateTeleportOneUserHomeRequest(IClientAPI client, UUID invoice, UUID senderID, UUID prey, bool kick) { if (prey == UUID.Zero) return; @@ -227,8 +227,20 @@ namespace OpenSim.Region.CoreModules.World.Estate ScenePresence p = scene.GetScenePresence(prey); if (p != null && !p.IsChildAgent && !p.IsDeleted && !p.IsInTransit) { - p.ControllingClient.SendTeleportStart(16); - scene.TeleportClientHome(prey, client); + if (kick) + { + p.ControllingClient.Kick("You have been kicked out"); + s.CloseAgent(p.UUID, false); + } + else + { + p.ControllingClient.SendTeleportStart(16); + if (!s.TeleportClientHome(prey, client)) + { + p.ControllingClient.Kick("You were teleported home by the region owner, but the TP failed"); + s.CloseAgent(p.UUID, false); + } + } return; } } @@ -259,6 +271,11 @@ namespace OpenSim.Region.CoreModules.World.Estate { p.ControllingClient.SendTeleportStart(16); scene.TeleportClientHome(p.ControllingClient.AgentId, client); + if (!s.TeleportClientHome(p.ControllingClient.AgentId, client)) + { + p.ControllingClient.Kick("You were teleported home by the region owner, but the TP failed - you have been logged out."); + s.CloseAgent(p.UUID, false); + } } }); } From 7c0eab8a23a4e2998b5035c7b5de04578e3896fb Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 20 Feb 2019 04:04:35 +0000 Subject: [PATCH 08/62] Xengine: don't lose state change events --- OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs index 019a0d9986..ef4d6ec81b 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs @@ -699,7 +699,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance // If min event delay is set then ignore any events untill the time has expired // This currently only allows 1 event of any type in the given time period. // This may need extending to allow for a time for each individual event type. - if (m_eventDelayTicks != 0) + if (m_eventDelayTicks != 0 && data.EventName != "state" && data.EventName != "state_entry" && data.EventName != "state_exit") { if (DateTime.Now.Ticks < m_nextEventTimeTicks) return; From 62fb0961fd030a735d2ff32703a7629de1f895ca Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 20 Feb 2019 04:07:42 +0000 Subject: [PATCH 09/62] missing file on the kick commit --- OpenSim/Framework/IClientAPI.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index 307dbf3917..e660dce2ca 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -390,7 +390,7 @@ namespace OpenSim.Framework IClientAPI remoteClient, UUID invoice, UUID senderID, bool scripted, bool collisionEvents, bool physics); public delegate void EstateTeleportOneUserHomeRequest( - IClientAPI remoteClient, UUID invoice, UUID senderID, UUID prey); + IClientAPI remoteClient, UUID invoice, UUID senderID, UUID prey, bool kill); public delegate void EstateTeleportAllUsersHomeRequest(IClientAPI remoteClient, UUID invoice, UUID senderID); From ac651a168abc2c69214d2957a6d6fa83abe9dec4 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 20 Feb 2019 04:54:32 +0000 Subject: [PATCH 10/62] Xengine: exclude a few more events from mineventdelay --- OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs index ef4d6ec81b..351fca9bed 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs @@ -699,7 +699,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance // If min event delay is set then ignore any events untill the time has expired // This currently only allows 1 event of any type in the given time period. // This may need extending to allow for a time for each individual event type. - if (m_eventDelayTicks != 0 && data.EventName != "state" && data.EventName != "state_entry" && data.EventName != "state_exit") + if (m_eventDelayTicks != 0 && + data.EventName != "state" && data.EventName != "state_entry" && data.EventName != "state_exit" + && data.EventName != "run_time_permissions" && data.EventName != "http_request" && data.EventName != "link_message") { if (DateTime.Now.Ticks < m_nextEventTimeTicks) return; From 72c472f98858a6609d12ad36791fe497cd9f21d5 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 20 Feb 2019 20:12:13 +0000 Subject: [PATCH 11/62] Yengine: let llminEventDelay do something: it just ignores a more limited set of events than Xengine (neither do as SL) --- .../YEngine/MMRScriptEventCode.cs | 5 +-- .../Region/ScriptEngine/YEngine/XMREngine.cs | 5 ++- .../ScriptEngine/YEngine/XMRInstBackend.cs | 12 +++---- .../ScriptEngine/YEngine/XMRInstMain.cs | 4 +++ .../ScriptEngine/YEngine/XMRInstMisc.cs | 18 ++++++++++ .../Region/ScriptEngine/YEngine/XMRInstRun.cs | 35 +++++++++++++++---- 6 files changed, 62 insertions(+), 17 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptEventCode.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptEventCode.cs index c00e8d4dda..3539fa13b9 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptEventCode.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptEventCode.cs @@ -88,10 +88,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine path_update = 40, - // XMRE specific - region_cross = 63, - // marks highest numbered event, ie, number of columns in seht. - Size = 64 + Size = 41 } } diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs b/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs index 017b294d6b..6acc293869 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs @@ -930,12 +930,15 @@ namespace OpenSim.Region.ScriptEngine.Yengine public void SetMinEventDelay(UUID itemID, double delay) { + XMRInstance instance = GetInstance(itemID); + if (instance != null) + instance.MinEventDelay = delay; } public int GetStartParameter(UUID itemID) { XMRInstance instance = GetInstance(itemID); - if(instance == null) + if (instance == null) return 0; return instance.StartParam; } diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs index 6fe11d8113..7fc97e9c1a 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs @@ -422,9 +422,9 @@ namespace OpenSim.Region.ScriptEngine.Yengine object[] saveEHArgs = this.ehArgs; ScriptEventCode saveEventCode = this.eventCode; - this.m_DetectParams = evt.DetectParams; - this.ehArgs = evt.Params; - this.eventCode = evc; + m_DetectParams = evt.DetectParams; + ehArgs = evt.Params; + eventCode = evc; try { @@ -432,9 +432,9 @@ namespace OpenSim.Region.ScriptEngine.Yengine } finally { - this.m_DetectParams = saveDetParams; - this.ehArgs = saveEHArgs; - this.eventCode = saveEventCode; + m_DetectParams = saveDetParams; + ehArgs = saveEHArgs; + eventCode = saveEventCode; } // Keep waiting until we find a returnable event or timeout. diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstMain.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstMain.cs index 3c0040c84a..def06b27c5 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMRInstMain.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstMain.cs @@ -215,5 +215,9 @@ namespace OpenSim.Region.ScriptEngine.Yengine // It's born ready, but will be reset when the detach is posted. // It will then be set again on suspend/completion private ManualResetEvent m_DetachReady = new ManualResetEvent(true); + + // llmineventdelay support + double m_minEventDelay = 0.0; + double m_nextEventTime = 0.0; } } diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstMisc.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstMisc.cs index 0af3d37c3a..12feb7b428 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMRInstMisc.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstMisc.cs @@ -298,6 +298,24 @@ namespace OpenSim.Region.ScriptEngine.Yengine } } + public double MinEventDelay + { + get + { + return m_minEventDelay; + } + set + { + if (value > 0.001) + m_minEventDelay = value; + else + m_minEventDelay = 0.0; + + m_nextEventTime = 0.0; // reset it + } + } + + public SceneObjectPart SceneObject { get diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs index 6c969dc697..9d73a7f52b 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs @@ -63,8 +63,7 @@ namespace OpenSim.Region.ScriptEngine.Yengine */ public void PostEvent(EventParams evt) { - ScriptEventCode evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), - evt.EventName); + ScriptEventCode evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName); // Put event on end of event queue. bool startIt = false; @@ -86,6 +85,32 @@ namespace OpenSim.Region.ScriptEngine.Yengine if(!m_Running && !construct) return; + if(m_minEventDelay != 0) + { + switch (evc) + { + // ignore some events by time set by llMinEventDelay + case ScriptEventCode.collision: + case ScriptEventCode.land_collision: + case ScriptEventCode.listen: + case ScriptEventCode.not_at_target: + case ScriptEventCode.not_at_rot_target: + case ScriptEventCode.no_sensor: + case ScriptEventCode.sensor: + case ScriptEventCode.timer: + case ScriptEventCode.touch: + { + double now = Util.GetTimeStamp(); + if (now < m_nextEventTime) + return; + m_nextEventTime = now + m_minEventDelay; + break; + } + default: + break; + } + } + // Only so many of each event type allowed to queue. if((uint)evc < (uint)m_EventCounts.Length) { @@ -124,10 +149,8 @@ namespace OpenSim.Region.ScriptEngine.Yengine for(lln2 = m_EventQueue.First; lln2 != null; lln2 = lln2.Next) { EventParams evt2 = lln2.Value; - ScriptEventCode evc2 = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), - evt2.EventName); - if((evc2 != ScriptEventCode.state_entry) && - (evc2 != ScriptEventCode.attach)) + ScriptEventCode evc2 = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt2.EventName); + if((evc2 != ScriptEventCode.state_entry) && (evc2 != ScriptEventCode.attach)) break; } if(lln2 == null) From 0f574d432d6a2dd4a15a9d6bba236a7cf2656454 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 20 Feb 2019 20:36:43 +0000 Subject: [PATCH 12/62] Yengine:save minEventDelay in script state --- OpenSim/Region/ScriptEngine/YEngine/XMRInstCapture.cs | 9 +++++++++ OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstCapture.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstCapture.cs index 9bb894de2c..5798638623 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMRInstCapture.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstCapture.cs @@ -115,6 +115,15 @@ namespace OpenSim.Region.ScriptEngine.Yengine m_RunOnePhase = "GetExecutionState D"; CheckRunLockInvariants(true); + if (m_minEventDelay != 0.0) + { + XmlElement minEventDelayN = doc.CreateElement("", "mEvtDly", ""); + minEventDelayN.AppendChild(doc.CreateTextNode(m_minEventDelay.ToString())); + scriptStateN.AppendChild(minEventDelayN); + m_RunOnePhase = "GetExecutionState D"; + CheckRunLockInvariants(true); + } + // More misc data. XmlNode permissionsN = doc.CreateElement("", "Permissions", ""); scriptStateN.AppendChild(permissionsN); diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs index b140453b83..7e13ae4962 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs @@ -527,6 +527,11 @@ namespace OpenSim.Region.ScriptEngine.Yengine XmlElement doGblInitN = (XmlElement)scriptStateN.SelectSingleNode("DoGblInit"); doGblInit = bool.Parse(doGblInitN.InnerText); + double minEventDelay = 0.0; + XmlElement minEventDelayN = (XmlElement)scriptStateN.SelectSingleNode("mEvtDly"); + if(minEventDelayN != null) + minEventDelay = Double.Parse(minEventDelayN.InnerText); + // get values used by stuff like llDetectedGrab, etc. DetectParams[] detParams = RestoreDetectParams(scriptStateN.SelectSingleNode("DetectArray")); @@ -576,6 +581,8 @@ namespace OpenSim.Region.ScriptEngine.Yengine AsyncCommandManager.CreateFromData(m_Engine, m_LocalID, m_ItemID, m_Part.UUID, pluginData); + + MinEventDelay = minEventDelay; } private void processXstate(XmlDocument doc) @@ -919,6 +926,8 @@ namespace OpenSim.Region.ScriptEngine.Yengine AsyncCommandManager.CreateFromData(m_Engine, m_LocalID, m_ItemID, m_Part.UUID, pluginData); + + MinEventDelay = minEventDelay; } private static void getvarNames(Dictionary s, Dictionary d) From 17ea412da1a38f8e09d95c5604694c595f111473 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 21 Feb 2019 21:54:24 +0000 Subject: [PATCH 13/62] fix last owner on add to object inventory --- .../Region/Framework/Scenes/SceneObjectGroup.Inventory.cs | 2 +- .../Region/Framework/Scenes/SceneObjectPartInventory.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs index bf217a5f3c..8899e96dba 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.Inventory.cs @@ -154,7 +154,7 @@ namespace OpenSim.Region.Framework.Scenes // We're adding this to a prim we don't own. Force // owner change taskItem.Flags |= (uint)InventoryItemFlags.ObjectSlamPerm; - + taskItem.LastOwnerID = item.Owner; } else { diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index 4934b8327c..bc9ab7f3a0 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -815,10 +815,10 @@ namespace OpenSim.Region.Framework.Scenes m_items.LockItemsForWrite(true); m_items.Add(item.ItemID, item); m_items.LockItemsForWrite(false); - if (allowedDrop) - m_part.TriggerScriptChangedEvent(Changed.ALLOWED_DROP); - else - m_part.TriggerScriptChangedEvent(Changed.INVENTORY); + if (allowedDrop) + m_part.TriggerScriptChangedEvent(Changed.ALLOWED_DROP); + else + m_part.TriggerScriptChangedEvent(Changed.INVENTORY); m_part.AggregateInnerPerms(); m_inventorySerial++; From 16596b6ad0af373cccde83d8faa01fa996c1023a Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 21 Feb 2019 21:56:27 +0000 Subject: [PATCH 14/62] add LSL_Key osGetInventoryLastOwner(string itemNameorid) --- .../Shared/Api/Implementation/OSSL_Api.cs | 21 +++++++++++++++++-- .../Shared/Api/Interface/IOSSL_Api.cs | 1 + .../Shared/Api/Runtime/OSSL_Stub.cs | 6 ++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index e114a53455..070176b131 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -4874,8 +4874,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return Math.Atan2(mcross, dot); } - -//******* link sound public void osAdjustSoundVolume(LSL_Integer linknum, LSL_Float volume) { m_host.AddScriptLPS(1); @@ -5406,5 +5404,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return 1; } + public LSL_Key osGetInventoryLastOwner(string itemNameorid) + { + m_host.AddScriptLPS(1); + + TaskInventoryItem item = null; + UUID itemID; + if (UUID.TryParse(itemNameorid, out itemID)) + item = m_host.Inventory.GetInventoryItem(itemID); + else + item = m_host.Inventory.GetInventoryItem(itemNameorid); + + if (item == null) + return UUID.Zero.ToString(); + + UUID id = item.LastOwnerID; + if(id == UUID.Zero) + id= item.OwnerID; + return id.ToString(); + } } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs index 67c0261625..727757783e 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -547,5 +547,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces LSL_Integer osApproxEquals(vector va, vector vb, LSL_Float margin); LSL_Integer osApproxEquals(rotation ra, rotation rb); LSL_Integer osApproxEquals(rotation ra, rotation rb, LSL_Float margin); + LSL_Key osGetInventoryLastOwner(string itemNameOrId); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index 76d334b915..bc64ac4ee4 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -1366,5 +1366,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase { return m_OSSL_Functions.osApproxEquals(ra, rb, margin); } + + public LSL_Key osGetInventoryLastOwner(string itemNameOrId) + { + return m_OSSL_Functions.osGetInventoryLastOwner(itemNameOrId); + } + } } From b7507b70bc71bbb935ccb1542cfbacbf5a3a273d Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 21 Feb 2019 22:05:38 +0000 Subject: [PATCH 15/62] Yengine changed position and shape events can bt throttled by mineventdelay --- OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs index 9d73a7f52b..1b735e33de 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstRun.cs @@ -106,6 +106,21 @@ namespace OpenSim.Region.ScriptEngine.Yengine m_nextEventTime = now + m_minEventDelay; break; } + case ScriptEventCode.changed: + { + const int canignore = ~(CHANGED_SCALE | CHANGED_POSITION); + int change = (int)evt.Params[0]; + if(change == 0) // what? + return; + if((change & canignore) == 0) + { + double now = Util.GetTimeStamp(); + if (now < m_nextEventTime) + return; + m_nextEventTime = now + m_minEventDelay; + } + break; + } default: break; } From ece38437983440449acbc7f7202c470e72ae4e61 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 21 Feb 2019 22:54:17 +0000 Subject: [PATCH 16/62] let changed allowed drop have lldetectedkey[0] return the dropped item id, Yengine and still testing" --- OpenSim/Region/Framework/Scenes/EventManager.cs | 6 +++--- .../Region/Framework/Scenes/SceneObjectPart.cs | 4 ++-- .../Scenes/SceneObjectPartInventory.cs | 2 +- .../Region/ScriptEngine/XEngine/EventManager.cs | 2 +- .../Region/ScriptEngine/YEngine/XMREvents.cs | 17 +++++++++++++++-- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs index f76f8828d0..edc8886fbf 100644 --- a/OpenSim/Region/Framework/Scenes/EventManager.cs +++ b/OpenSim/Region/Framework/Scenes/EventManager.cs @@ -539,7 +539,7 @@ namespace OpenSim.Region.Framework.Scenes /// /// public event ScriptChangedEvent OnScriptChangedEvent; - public delegate void ScriptChangedEvent(uint localID, uint change); + public delegate void ScriptChangedEvent(uint localID, uint change, object data); public delegate void ScriptControlEvent(UUID item, UUID avatarID, uint held, uint changed); @@ -1185,7 +1185,7 @@ namespace OpenSim.Region.Framework.Scenes } } - public void TriggerOnScriptChangedEvent(uint localID, uint change) + public void TriggerOnScriptChangedEvent(uint localID, uint change, object parameter = null) { ScriptChangedEvent handlerScriptChangedEvent = OnScriptChangedEvent; if (handlerScriptChangedEvent != null) @@ -1194,7 +1194,7 @@ namespace OpenSim.Region.Framework.Scenes { try { - d(localID, change); + d(localID, change, parameter); } catch (Exception e) { diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index a23ebbfa46..23bef74704 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -4439,10 +4439,10 @@ namespace OpenSim.Region.Framework.Scenes SceneObjectSerializer.SOPToXml2(xmlWriter, this, new Dictionary()); } - public void TriggerScriptChangedEvent(Changed val) + public void TriggerScriptChangedEvent(Changed val, object data = null) { if (ParentGroup != null && ParentGroup.Scene != null) - ParentGroup.Scene.EventManager.TriggerOnScriptChangedEvent(LocalId, (uint)val); + ParentGroup.Scene.EventManager.TriggerOnScriptChangedEvent(LocalId, (uint)val, data); } public void TrimPermissions() diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs index bc9ab7f3a0..a0f895933f 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPartInventory.cs @@ -816,7 +816,7 @@ namespace OpenSim.Region.Framework.Scenes m_items.Add(item.ItemID, item); m_items.LockItemsForWrite(false); if (allowedDrop) - m_part.TriggerScriptChangedEvent(Changed.ALLOWED_DROP); + m_part.TriggerScriptChangedEvent(Changed.ALLOWED_DROP, item.ItemID); else m_part.TriggerScriptChangedEvent(Changed.INVENTORY); diff --git a/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs b/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs index b7fc161e61..7c2136edd6 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs @@ -215,7 +215,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine det)); } - public void changed(uint localID, uint change) + public void changed(uint localID, uint change, object parameter) { // Add to queue for all scripts in localID, Object pass change. myScriptEngine.PostObjectEvent(localID, new EventParams( diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs b/OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs index 5a8b2a39aa..eb5aeeb86d 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs @@ -182,13 +182,26 @@ namespace OpenSim.Region.ScriptEngine.Yengine new DetectParams[] { det })); } - public void changed(uint localID, uint change) + public void changed(uint localID, uint change, object data) { int ch = (int)change; // Add to queue for all scripts in localID, Object pass change. - this.PostObjectEvent(localID, new EventParams( + if(data == null) + { + PostObjectEvent(localID, new EventParams( "changed", new object[] { ch }, zeroDetectParams)); + return; + } + if ( data is UUID) + { + DetectParams det = new DetectParams(); + det.Key = (UUID)data; + PostObjectEvent(localID, new EventParams( + "changed", new object[] { ch }, + new DetectParams[] { det })); + return; + } } // state_entry: not processed here From bd27573130d4a40d678c81c687591708ab4e4f34 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 21 Feb 2019 23:11:03 +0000 Subject: [PATCH 17/62] add LSL_String osGetInventoryName(LSL_Key itemId) and LSL_String osGetInventoryDescription(LSL_String itemNameOrId) --- .../Shared/Api/Implementation/OSSL_Api.cs | 34 ++++++++++++++++++- .../Shared/Api/Interface/IOSSL_Api.cs | 4 ++- .../Shared/Api/Runtime/OSSL_Stub.cs | 11 +++++- 3 files changed, 46 insertions(+), 3 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 070176b131..e4cc3aa18a 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -5404,7 +5404,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return 1; } - public LSL_Key osGetInventoryLastOwner(string itemNameorid) + public LSL_Key osGetInventoryLastOwner(LSL_String itemNameorid) { m_host.AddScriptLPS(1); @@ -5423,5 +5423,37 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api id= item.OwnerID; return id.ToString(); } + + public LSL_String osGetInventoryName(LSL_Key itemId) + { + m_host.AddScriptLPS(1); + + TaskInventoryItem item = null; + UUID itemID; + if (UUID.TryParse(itemId, out itemID)) + item = m_host.Inventory.GetInventoryItem(itemID); + + if (item == null) + return String.Empty; + + return item.Name; + } + + public LSL_String osGetInventoryDescription(LSL_String itemNameorid) + { + m_host.AddScriptLPS(1); + + TaskInventoryItem item = null; + UUID itemID; + if (UUID.TryParse(itemNameorid, out itemID)) + item = m_host.Inventory.GetInventoryItem(itemID); + else + item = m_host.Inventory.GetInventoryItem(itemNameorid); + + if (item == null) + return String.Empty; + + return item.Description; + } } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs index 727757783e..711108b124 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -547,6 +547,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces LSL_Integer osApproxEquals(vector va, vector vb, LSL_Float margin); LSL_Integer osApproxEquals(rotation ra, rotation rb); LSL_Integer osApproxEquals(rotation ra, rotation rb, LSL_Float margin); - LSL_Key osGetInventoryLastOwner(string itemNameOrId); + LSL_Key osGetInventoryLastOwner(LSL_String itemNameOrId); + LSL_String osGetInventoryName(LSL_Key itemId); + LSL_String osGetInventoryDescription(LSL_String itemNameOrId); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index bc64ac4ee4..86c8ba3bc6 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -1367,10 +1367,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase return m_OSSL_Functions.osApproxEquals(ra, rb, margin); } - public LSL_Key osGetInventoryLastOwner(string itemNameOrId) + public LSL_Key osGetInventoryLastOwner(LSL_String itemNameOrId) { return m_OSSL_Functions.osGetInventoryLastOwner(itemNameOrId); } + public LSL_String osGetInventoryName(LSL_Key itemId) + { + return m_OSSL_Functions.osGetInventoryName(itemId); + } + + public LSL_String osGetInventoryDescription(LSL_String itemNameOrId) + { + return m_OSSL_Functions.osGetInventoryDescription(itemNameOrId); + } } } From 5d78f52f7bd3aa27385a800ecae35d3579d3bee8 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 21 Feb 2019 23:17:53 +0000 Subject: [PATCH 18/62] let Xengine also have that detectedkey --- .../Region/ScriptEngine/XEngine/EventManager.cs | 17 +++++++++++++++-- .../Region/ScriptEngine/YEngine/XMREvents.cs | 8 ++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs b/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs index 7c2136edd6..50a95a90ee 100644 --- a/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs +++ b/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs @@ -218,9 +218,22 @@ namespace OpenSim.Region.ScriptEngine.XEngine public void changed(uint localID, uint change, object parameter) { // Add to queue for all scripts in localID, Object pass change. - myScriptEngine.PostObjectEvent(localID, new EventParams( - "changed",new object[] { new LSL_Types.LSLInteger(change) }, + if(parameter == null) + { + myScriptEngine.PostObjectEvent(localID, new EventParams( + "changed", new object[] { new LSL_Types.LSLInteger(change) }, new DetectParams[0])); + return; + } + if (parameter is UUID) + { + DetectParams det = new DetectParams(); + det.Key = (UUID)parameter; + myScriptEngine.PostObjectEvent(localID, new EventParams( + "changed", new object[] { new LSL_Types.LSLInteger(change) }, + new DetectParams[] { det })); + return; + } } // state_entry: not processed here diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs b/OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs index eb5aeeb86d..65a8aed88b 100644 --- a/OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs +++ b/OpenSim/Region/ScriptEngine/YEngine/XMREvents.cs @@ -182,21 +182,21 @@ namespace OpenSim.Region.ScriptEngine.Yengine new DetectParams[] { det })); } - public void changed(uint localID, uint change, object data) + public void changed(uint localID, uint change, object parameter) { int ch = (int)change; // Add to queue for all scripts in localID, Object pass change. - if(data == null) + if(parameter == null) { PostObjectEvent(localID, new EventParams( "changed", new object[] { ch }, zeroDetectParams)); return; } - if ( data is UUID) + if ( parameter is UUID) { DetectParams det = new DetectParams(); - det.Key = (UUID)data; + det.Key = (UUID)parameter; PostObjectEvent(localID, new EventParams( "changed", new object[] { ch }, new DetectParams[] { det })); From 6e05695244173435067408e5716cb4bd2a2c0aef Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 21 Feb 2019 23:41:49 +0000 Subject: [PATCH 19/62] add LSL_Key osGetLastChangedEventKey() ( it is a alias for lldetectedkey(0) but don't tell anyone --- .../ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs | 9 +++++++++ .../ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs | 1 + .../Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index e4cc3aa18a..7674628276 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -5455,5 +5455,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return item.Description; } + + public LSL_Key osGetLastChangedEventKey() + { + m_host.AddScriptLPS(1); + DetectParams detectedParams = m_ScriptEngine.GetDetectParams(m_item.ItemID, 0); + if (detectedParams == null) + return String.Empty; + return detectedParams.Key.ToString(); + } } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs index 711108b124..8333af848d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -550,5 +550,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces LSL_Key osGetInventoryLastOwner(LSL_String itemNameOrId); LSL_String osGetInventoryName(LSL_Key itemId); LSL_String osGetInventoryDescription(LSL_String itemNameOrId); + LSL_Key osGetLastChangedEventKey(); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index 86c8ba3bc6..f0fb745fe4 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -1381,5 +1381,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase { return m_OSSL_Functions.osGetInventoryDescription(itemNameOrId); } + + public LSL_Key osGetLastChangedEventKey() + { + return m_OSSL_Functions.osGetLastChangedEventKey(); + } } } From d89a089ca266311568febc367f41f4217b7580d1 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 22 Feb 2019 22:28:47 +0000 Subject: [PATCH 20/62] remove redundant osInventoryDescription() and improve osInventoryDesc() to do the same --- .../Shared/Api/Implementation/OSSL_Api.cs | 25 +------------------ .../Shared/Api/Interface/IOSSL_Api.cs | 4 +-- .../Shared/Api/Runtime/OSSL_Stub.cs | 9 ++----- 3 files changed, 4 insertions(+), 34 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index 7674628276..dabd399b0a 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -3889,29 +3889,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return date.ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ"); } - /// - /// Get the description from an inventory item - /// - /// - /// Item description - public LSL_String osGetInventoryDesc(string item) - { - CheckThreatLevel(); - - lock (m_host.TaskInventory) - { - foreach (KeyValuePair inv in m_host.TaskInventory) - { - if (inv.Value.Name == item) - { - return inv.Value.Description.ToString(); - } - } - } - - return String.Empty; - } - /// /// Invite user to the group this object is set to /// @@ -5439,7 +5416,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return item.Name; } - public LSL_String osGetInventoryDescription(LSL_String itemNameorid) + public LSL_String osGetInventoryDesc(LSL_String itemNameorid) { m_host.AddScriptLPS(1); diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs index 8333af848d..49b3f74e8c 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/IOSSL_Api.cs @@ -401,8 +401,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces LSL_String osUnixTimeToTimestamp(LSL_Integer time); - LSL_String osGetInventoryDesc(string item); - LSL_Integer osInviteToGroup(LSL_Key agentId); LSL_Integer osEjectFromGroup(LSL_Key agentId); @@ -549,7 +547,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces LSL_Integer osApproxEquals(rotation ra, rotation rb, LSL_Float margin); LSL_Key osGetInventoryLastOwner(LSL_String itemNameOrId); LSL_String osGetInventoryName(LSL_Key itemId); - LSL_String osGetInventoryDescription(LSL_String itemNameOrId); + LSL_String osGetInventoryDesc(LSL_String itemNameOrId); LSL_Key osGetLastChangedEventKey(); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs index f0fb745fe4..fb491e4923 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/OSSL_Stub.cs @@ -1060,11 +1060,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase return m_OSSL_Functions.osUnixTimeToTimestamp(time); } - public LSL_String osGetInventoryDesc(string item) - { - return m_OSSL_Functions.osGetInventoryDesc(item); - } - public LSL_Integer osInviteToGroup(LSL_Key agentId) { return m_OSSL_Functions.osInviteToGroup(agentId); @@ -1377,9 +1372,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase return m_OSSL_Functions.osGetInventoryName(itemId); } - public LSL_String osGetInventoryDescription(LSL_String itemNameOrId) + public LSL_String osGetInventoryDesc(LSL_String itemNameOrId) { - return m_OSSL_Functions.osGetInventoryDescription(itemNameOrId); + return m_OSSL_Functions.osGetInventoryDesc(itemNameOrId); } public LSL_Key osGetLastChangedEventKey() From 009af3122f8005ae5b8ed82f8ed1da89e9843e90 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 22 Feb 2019 22:42:07 +0000 Subject: [PATCH 21/62] update script sintaxe --- .../Shared/Api/Runtime/LSL_Constants.cs | 2 +- bin/ScriptSyntax.xml | 108 +++++++++++------- 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs index 49f46b7b4c..8b70128e71 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs @@ -35,7 +35,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase public partial class ScriptBaseClass { // SCRIPTS CONSTANTS - public static readonly LSLInteger OS_APIVERSION = 2; + public static readonly LSLInteger OS_APIVERSION = 3; public static readonly LSLInteger TRUE = 1; public static readonly LSLInteger FALSE = 0; diff --git a/bin/ScriptSyntax.xml b/bin/ScriptSyntax.xml index 862a66df93..8f2f6002f9 100644 --- a/bin/ScriptSyntax.xml +++ b/bin/ScriptSyntax.xml @@ -1,4 +1,4 @@ -a0b4b514-3c14-6b98-ca98-e18a79e9792f +20392e48-fad2-094e-bc5b-cda003a1e940 llsd-lsl-syntax-version2 controls @@ -1513,7 +1513,7 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f OS_APIVERSION typeinteger - value2 + value3 OS_ATTACH_MSG_ALL typeinteger @@ -6114,17 +6114,8 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f returninteger arguments - vatypevector - vbtypevector - margintypefloat - - - osApproxEquals - - returninteger - arguments - vatypevector - vbtypevector + atypefloat + btypefloat osApproxEquals @@ -6140,8 +6131,17 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f returninteger arguments - atypefloat - btypefloat + ratyperotation + rbtyperotation + + + osApproxEquals + + returninteger + arguments + vatypevector + vbtypevector + margintypefloat osApproxEquals @@ -6157,8 +6157,8 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f returninteger arguments - ratyperotation - rbtyperotation + vatypevector + vbtypevector osAvatarName2Key @@ -6186,14 +6186,14 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f osCauseDamage arguments - avatartypestring + avatartypekey damagetypefloat osCauseHealing arguments - avatartypestring + agentIdtypekey healingtypefloat @@ -6284,8 +6284,6 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f returnstring arguments drawListtypestring - startXtypeinteger - startYtypeinteger endXtypeinteger endYtypeinteger @@ -6295,6 +6293,8 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f returnstring arguments drawListtypestring + startXtypeinteger + startYtypeinteger endXtypeinteger endYtypeinteger @@ -6539,14 +6539,14 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f returnfloat arguments - avatartypestring + agentIdtypekey osGetHealth returnfloat arguments - avatartypestring + agentIdtypekey osGetInertiaData @@ -6558,9 +6558,28 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f returnstring arguments - itemtypestring + itemNameOrIdtypestring + osGetInventoryLastOwner + + returnkey + arguments + itemNameOrIdtypestring + + + osGetInventoryName + + returnstring + arguments + itemIdtypekey + + + osGetLastChangedEventKey + + returnkey + arguments + osGetLinkNumber returninteger @@ -6735,6 +6754,13 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f alerttypestring + osKickAvatar + + arguments + agentIdtypekey + alerttypestring + + osLoadedCreationDate returnstring @@ -6839,7 +6865,6 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f nametypestring positiontypevector notecardtypestring - optionstypeinteger osNpcCreate @@ -6850,6 +6875,7 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f nametypestring positiontypevector notecardtypestring + optionstypeinteger osNpcGetOwner @@ -7089,7 +7115,6 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f returninteger arguments secondstypefloat - msgtypestring osRegionRestart @@ -7097,6 +7122,7 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f returninteger arguments secondstypefloat + msgtypestring osReplaceString @@ -7199,14 +7225,14 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f osSetHealRate arguments - avatartypestring + agentIdtypekey healthtypefloat osSetHealth arguments - avatartypestring + agentIdtypekey healthtypefloat @@ -7432,6 +7458,8 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f arguments srctypestring valuetypestring + starttypeinteger + counttypeinteger ignorecasetypeinteger @@ -7441,8 +7469,6 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f arguments srctypestring valuetypestring - starttypeinteger - counttypeinteger ignorecasetypeinteger @@ -7525,15 +7551,6 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f osTeleportAgent - - arguments - agenttypestring - regionNametypestring - positiontypevector - lookattypevector - - - osTeleportAgent arguments agenttypestring @@ -7551,6 +7568,15 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f lookattypevector + osTeleportAgent + + arguments + agenttypestring + regionNametypestring + positiontypevector + lookattypevector + + osTeleportObject returninteger @@ -7564,8 +7590,6 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f osTeleportOwner arguments - regionXtypeinteger - regionYtypeinteger positiontypevector lookattypevector @@ -7581,6 +7605,8 @@ a0b4b514-3c14-6b98-ca98-e18a79e9792f osTeleportOwner arguments + regionXtypeinteger + regionYtypeinteger positiontypevector lookattypevector From 800f6d6529516be22d4ee79cd8e9161479f4df34 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sun, 24 Feb 2019 07:25:50 +0000 Subject: [PATCH 22/62] several changes to llHttpRequest processing: options section renamed ScriptsHttpRequestModule; throttle by prim (PrimRequestsBurst = 3, PrimRequestsPerSec = 1) and max concurrent connections per instance (MaxPoolThreads = 5), llhttprequest returns if error, nullkey if throttled, reqid otherwise --- .../HttpRequest/ScriptsHttpRequests.cs | 165 +++++++++++------- .../Framework/Interfaces/IHttpRequests.cs | 1 + .../Shared/Api/Implementation/LSL_Api.cs | 19 +- .../Api/Implementation/Plugins/HttpRequest.cs | 13 +- 4 files changed, 117 insertions(+), 81 deletions(-) diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs index c3f6d6b2df..54936a3133 100644 --- a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs @@ -27,6 +27,7 @@ using System; using System.Collections.Generic; +using System.Collections.Concurrent; using System.IO; using System.Net; using System.Net.Mail; @@ -101,11 +102,22 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest private OutboundUrlFilter m_outboundUrlFilter; private string m_proxyurl = ""; private string m_proxyexcepts = ""; + + private float m_primpersec = 1.0f; + private float m_primburst = 3f; + + private struct ThrottleData + { + public double lastTime; + public float count; + } // private Dictionary m_pendingRequests; + private ConcurrentQueue m_CompletedRequests; + private ConcurrentDictionary m_RequestsThrottle; + private Scene m_scene; - // private Queue rpcQueue = new Queue(); public static SmartThreadPool ThreadPool = null; public HttpRequestModule() @@ -119,6 +131,41 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest return UUID.Zero; } + public bool CheckThrottle(uint localID) + { + ThrottleData th; + double now = Util.GetTimeStamp(); + bool ret = false; + + if (m_RequestsThrottle.TryGetValue(localID, out th)) + { + double delta = now - th.lastTime; + th.lastTime = now; + + float add = (float)(m_primpersec * delta); + th.count += add; + if (th.count > m_primburst) + th.count = m_primburst; + + ret = th.count > 0; + } + else + { + th = new ThrottleData() + { + lastTime = now, + count = m_primburst + }; + ret = true; + } + + if (ret) + th.count--; + + m_RequestsThrottle[localID] = th; + return ret; + } + public UUID StartHttpRequest( uint localID, UUID itemID, string url, List parameters, Dictionary headers, string body, out HttpInitialRequestStatus status) @@ -243,9 +290,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest return false; lock (HttpListLock) - { m_pendingRequests.Add(req.ReqID, req); - } req.Process(); @@ -256,14 +301,19 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest { if (m_pendingRequests != null) { + List toremove = new List(); lock (HttpListLock) { - HttpRequestClass tmpReq; - if (m_pendingRequests.TryGetValue(m_itemID, out tmpReq)) + foreach (HttpRequestClass tmpReq in m_pendingRequests.Values) { - tmpReq.Stop(); - m_pendingRequests.Remove(m_itemID); + if(tmpReq.ItemID == m_itemID) + { + tmpReq.Stop(); + toremove.Add(tmpReq.ReqID); + } } + foreach(UUID id in toremove) + m_pendingRequests.Remove(id); } } } @@ -276,37 +326,37 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest * finished. I thought about setting up a queue for this, but * it will need some refactoring and this works 'enough' right now */ + public void GotCompletedRequest(HttpRequestClass req) + { + if(req.Removed) + return; + lock (HttpListLock) + { + m_pendingRequests.Remove(req.ReqID); + m_CompletedRequests.Enqueue(req); + } + } public IServiceRequest GetNextCompletedRequest() { - lock (HttpListLock) + HttpRequestClass req; + while(m_CompletedRequests.TryDequeue(out req)) { - foreach (UUID luid in m_pendingRequests.Keys) - { - HttpRequestClass tmpReq; - - if (m_pendingRequests.TryGetValue(luid, out tmpReq)) - { - if (tmpReq.Finished) - { - return tmpReq; - } - } - } + if(!req.Removed) + return req; } return null; } - public void RemoveCompletedRequest(UUID id) + public void RemoveCompletedRequest(UUID reqId) { lock (HttpListLock) { HttpRequestClass tmpReq; - if (m_pendingRequests.TryGetValue(id, out tmpReq)) + if (m_pendingRequests.TryGetValue(reqId, out tmpReq)) { tmpReq.Stop(); - tmpReq = null; - m_pendingRequests.Remove(id); + m_pendingRequests.Remove(reqId); } } } @@ -322,17 +372,20 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest HttpRequestClass.HttpBodyMaxLenMAX = config.Configs["Network"].GetInt("HttpBodyMaxLenMAX", 16384); - m_outboundUrlFilter = new OutboundUrlFilter("Script HTTP request module", config); - int maxThreads = 15; - IConfig httpConfig = config.Configs["HttpRequestModule"]; + int maxThreads = 5; + IConfig httpConfig = config.Configs["ScriptsHttpRequestModule"]; if (httpConfig != null) { maxThreads = httpConfig.GetInt("MaxPoolThreads", maxThreads); + m_primburst = httpConfig.GetFloat("PrimRequestsBurst", m_primburst); + m_primpersec = httpConfig.GetFloat("PrimRequestsPerSec", m_primpersec); } m_pendingRequests = new Dictionary(); + m_CompletedRequests = new ConcurrentQueue(); + m_RequestsThrottle = new ConcurrentDictionary(); // First instance sets this up for all sims if (ThreadPool == null) @@ -406,11 +459,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest /// public HttpRequestModule RequestModule { get; set; } - private bool _finished; - public bool Finished - { - get { return _finished; } - } + public bool Finished { get; private set;} + public bool Removed{ get; set;} public static int HttpBodyMaxLenMAX = 16384; @@ -427,19 +477,10 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest public bool HttpPragmaNoCache = true; // Request info - private UUID _itemID; - public UUID ItemID - { - get { return _itemID; } - set { _itemID = value; } - } - private uint _localID; - public uint LocalID - { - get { return _localID; } - set { _localID = value; } - } - public DateTime Next; + public UUID ReqID { get; set; } + public UUID ItemID { get; set;} + public uint LocalID { get; set;} + public string proxyurl; public string proxyexcepts; @@ -454,12 +495,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest public int MaxRedirects { get; set; } public string OutboundBody; - private UUID _reqID; - public UUID ReqID - { - get { return _reqID; } - set { _reqID = value; } - } + public HttpWebRequest Request; public string ResponseBody; public List ResponseMetadata; @@ -469,10 +505,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest public void Process() { - _finished = false; - - lock (HttpRequestModule.ThreadPool) - WorkItem = HttpRequestModule.ThreadPool.QueueWorkItem(new WorkItemCallback(StpSendWrapper), null); + WorkItem = HttpRequestModule.ThreadPool.QueueWorkItem(new WorkItemCallback(StpSendWrapper), null); } private object StpSendWrapper(object o) @@ -521,6 +554,9 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest public void SendRequest() { + if(Removed) + return; + HttpWebResponse response = null; Stream resStream = null; byte[] buf = new byte[HttpBodyMaxLenMAX + 16]; @@ -672,7 +708,6 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest if (response != null) response.Close(); - // We need to resubmit if ( (Status == (int)HttpStatusCode.MovedPermanently @@ -684,7 +719,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest { Status = (int)OSHttpStatusCode.ClientErrorJoker; ResponseBody = "Number of redirects exceeded max redirects"; - _finished = true; + WorkItem = null; + RequestModule.GotCompletedRequest(this); } else { @@ -694,13 +730,15 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest { Status = (int)OSHttpStatusCode.ClientErrorJoker; ResponseBody = "HTTP redirect code but no location header"; - _finished = true; + WorkItem = null; + RequestModule.GotCompletedRequest(this); } else if (!RequestModule.CheckAllowed(new Uri(location))) { Status = (int)OSHttpStatusCode.ClientErrorJoker; ResponseBody = "URL from HTTP redirect blocked: " + location; - _finished = true; + WorkItem = null; + RequestModule.GotCompletedRequest(this); } else { @@ -717,9 +755,10 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest } else { - _finished = true; + WorkItem = null; if (ResponseBody == null) ResponseBody = String.Empty; + RequestModule.GotCompletedRequest(this); } } } @@ -728,10 +767,12 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest { try { + Removed = true; + if(WorkItem == null) + return; + if (!WorkItem.Cancel()) - { WorkItem.Cancel(true); - } } catch (Exception) { diff --git a/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs b/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs index 978c248610..3ab1f6ccca 100644 --- a/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs +++ b/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs @@ -87,5 +87,6 @@ namespace OpenSim.Region.Framework.Interfaces void StopHttpRequest(uint m_localID, UUID m_itemID); IServiceRequest GetNextCompletedRequest(); void RemoveCompletedRequest(UUID id); + bool CheckThrottle(uint localID); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 943141cba3..6cea821e0a 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -13945,14 +13945,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Key llHTTPRequest(string url, LSL_List parameters, string body) { - // Partial implementation: support for parameter flags needed - // see http://wiki.secondlife.com/wiki/LlHTTPRequest - // parameter flags support are implemented in ScriptsHttpRequests.cs - // in StartHttpRequest - m_host.AddScriptLPS(1); - IHttpRequestModule httpScriptMod = - m_ScriptEngine.World.RequestModuleInterface(); + IHttpRequestModule httpScriptMod = m_ScriptEngine.World.RequestModuleInterface(); + if(httpScriptMod == null) + return ""; + + if(!httpScriptMod.CheckThrottle(m_host.LocalId)) + return UUID.Zero.ToString(); + List param = new List(); bool ok; Int32 flag; @@ -14123,8 +14123,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } HttpInitialRequestStatus status; - UUID reqID - = httpScriptMod.StartHttpRequest(m_host.LocalId, m_item.ItemID, url, param, httpHeaders, body, out status); + UUID reqID = httpScriptMod.StartHttpRequest(m_host.LocalId, m_item.ItemID, url, param, httpHeaders, body, out status); if (status == HttpInitialRequestStatus.DISALLOWED_BY_FILTER) Error("llHttpRequest", string.Format("Request to {0} disallowed by filter", url)); @@ -14132,7 +14131,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (reqID != UUID.Zero) return reqID.ToString(); else - return null; + return ""; } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/HttpRequest.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/HttpRequest.cs index 629b14bbb2..166f2d9814 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/HttpRequest.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/HttpRequest.cs @@ -48,14 +48,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins if (m_CmdManager.m_ScriptEngine.World == null) return; - IHttpRequestModule iHttpReq = - m_CmdManager.m_ScriptEngine.World.RequestModuleInterface(); - - HttpRequestClass httpInfo = null; - - if (iHttpReq != null) - httpInfo = (HttpRequestClass)iHttpReq.GetNextCompletedRequest(); + IHttpRequestModule iHttpReq = m_CmdManager.m_ScriptEngine.World.RequestModuleInterface(); + if(iHttpReq == null) + return; + HttpRequestClass httpInfo = (HttpRequestClass)iHttpReq.GetNextCompletedRequest(); while (httpInfo != null) { //m_log.Debug("[AsyncLSL]:" + httpInfo.response_body + httpInfo.status); @@ -67,8 +64,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins // implemented here yet anyway. Should be fixed if/when maxsize // is supported - iHttpReq.RemoveCompletedRequest(httpInfo.ReqID); - object[] resobj = new object[] { new LSL_Types.LSLString(httpInfo.ReqID.ToString()), From cd5a6daa84a153fdc3dc7cc3a65363b36d441b34 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sun, 24 Feb 2019 09:46:55 +0000 Subject: [PATCH 23/62] also throttle llhttprequest by owner, options: PrimOwnerRequestsBurst = 5, PrimOwnerRequestsPerSec = 25; increase concurrency to 8 --- .../HttpRequest/ScriptsHttpRequests.cs | 154 ++++++++++-------- .../Framework/Interfaces/IHttpRequests.cs | 2 +- .../Shared/Api/Implementation/LSL_Api.cs | 2 +- 3 files changed, 87 insertions(+), 71 deletions(-) diff --git a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs index 54936a3133..83d91c40b5 100644 --- a/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs +++ b/OpenSim/Region/CoreModules/Scripting/HttpRequest/ScriptsHttpRequests.cs @@ -95,16 +95,18 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - private object HttpListLock = new object(); - private int httpTimeout = 30000; + private object m_httpListLock = new object(); + private int m_httpTimeout = 30000; private string m_name = "HttpScriptRequests"; private OutboundUrlFilter m_outboundUrlFilter; private string m_proxyurl = ""; private string m_proxyexcepts = ""; - - private float m_primpersec = 1.0f; - private float m_primburst = 3f; + + private float m_primPerSec = 1.0f; + private float m_primBurst = 3.0f; + private float m_primOwnerPerSec = 25.0f; + private float m_primOwnerBurst = 5.0f; private struct ThrottleData { @@ -116,8 +118,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest private Dictionary m_pendingRequests; private ConcurrentQueue m_CompletedRequests; private ConcurrentDictionary m_RequestsThrottle; + private ConcurrentDictionary m_OwnerRequestsThrottle; - private Scene m_scene; public static SmartThreadPool ThreadPool = null; public HttpRequestModule() @@ -131,38 +133,64 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest return UUID.Zero; } - public bool CheckThrottle(uint localID) + public bool CheckThrottle(uint localID, UUID ownerID) { ThrottleData th; double now = Util.GetTimeStamp(); - bool ret = false; + bool ret; if (m_RequestsThrottle.TryGetValue(localID, out th)) { double delta = now - th.lastTime; th.lastTime = now; - float add = (float)(m_primpersec * delta); + float add = (float)(m_primPerSec * delta); th.count += add; - if (th.count > m_primburst) - th.count = m_primburst; + if (th.count > m_primBurst) + th.count = m_primBurst; ret = th.count > 0; + if (ret) + th.count--; } else { th = new ThrottleData() { lastTime = now, - count = m_primburst + count = m_primBurst - 1 }; ret = true; } - - if (ret) - th.count--; - m_RequestsThrottle[localID] = th; + + if(!ret) + return false; + + if (m_OwnerRequestsThrottle.TryGetValue(ownerID, out th)) + { + double delta = now - th.lastTime; + th.lastTime = now; + + float add = (float)(m_primOwnerPerSec * delta); + th.count += add; + if (th.count > m_primOwnerBurst) + th.count = m_primOwnerBurst; + + ret = th.count > 0; + if (ret) + th.count--; + } + else + { + th = new ThrottleData() + { + lastTime = now, + count = m_primOwnerBurst - 1 + }; + } + m_OwnerRequestsThrottle[ownerID] = th; + return ret; } @@ -170,6 +198,12 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest uint localID, UUID itemID, string url, List parameters, Dictionary headers, string body, out HttpInitialRequestStatus status) { + if (!CheckAllowed(new Uri(url))) + { + status = HttpInitialRequestStatus.DISALLOWED_BY_FILTER; + return UUID.Zero; + } + UUID reqID = UUID.Random(); HttpRequestClass htc = new HttpRequestClass(); @@ -254,7 +288,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest htc.ItemID = itemID; htc.Url = url; htc.ReqID = reqID; - htc.HttpTimeout = httpTimeout; + htc.HttpTimeout = m_httpTimeout; htc.OutboundBody = body; htc.ResponseHeaders = headers; htc.proxyurl = m_proxyurl; @@ -263,16 +297,12 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest // Same number as default HttpWebRequest.MaximumAutomaticRedirections htc.MaxRedirects = 50; - if (StartHttpRequest(htc)) - { - status = HttpInitialRequestStatus.OK; - return htc.ReqID; - } - else - { - status = HttpInitialRequestStatus.DISALLOWED_BY_FILTER; - return UUID.Zero; - } + lock (m_httpListLock) + m_pendingRequests.Add(reqID, htc); + + htc.Process(); + status = HttpInitialRequestStatus.OK; + return reqID; } /// @@ -284,37 +314,21 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest return m_outboundUrlFilter.CheckAllowed(url); } - public bool StartHttpRequest(HttpRequestClass req) - { - if (!CheckAllowed(new Uri(req.Url))) - return false; - - lock (HttpListLock) - m_pendingRequests.Add(req.ReqID, req); - - req.Process(); - - return true; - } - public void StopHttpRequest(uint m_localID, UUID m_itemID) { - if (m_pendingRequests != null) + List toremove = new List(); + lock (m_httpListLock) { - List toremove = new List(); - lock (HttpListLock) + foreach (HttpRequestClass tmpReq in m_pendingRequests.Values) { - foreach (HttpRequestClass tmpReq in m_pendingRequests.Values) + if(tmpReq.ItemID == m_itemID) { - if(tmpReq.ItemID == m_itemID) - { - tmpReq.Stop(); - toremove.Add(tmpReq.ReqID); - } + tmpReq.Stop(); + toremove.Add(tmpReq.ReqID); } - foreach(UUID id in toremove) - m_pendingRequests.Remove(id); } + foreach(UUID id in toremove) + m_pendingRequests.Remove(id); } } @@ -328,10 +342,10 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest */ public void GotCompletedRequest(HttpRequestClass req) { - if(req.Removed) - return; - lock (HttpListLock) + lock (m_httpListLock) { + if (req.Removed) + return; m_pendingRequests.Remove(req.ReqID); m_CompletedRequests.Enqueue(req); } @@ -340,17 +354,15 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest public IServiceRequest GetNextCompletedRequest() { HttpRequestClass req; - while(m_CompletedRequests.TryDequeue(out req)) - { - if(!req.Removed) - return req; - } + if(m_CompletedRequests.TryDequeue(out req)) + return req; + return null; } public void RemoveCompletedRequest(UUID reqId) { - lock (HttpListLock) + lock (m_httpListLock) { HttpRequestClass tmpReq; if (m_pendingRequests.TryGetValue(reqId, out tmpReq)) @@ -374,18 +386,26 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest m_outboundUrlFilter = new OutboundUrlFilter("Script HTTP request module", config); - int maxThreads = 5; + int maxThreads = 8; IConfig httpConfig = config.Configs["ScriptsHttpRequestModule"]; if (httpConfig != null) { maxThreads = httpConfig.GetInt("MaxPoolThreads", maxThreads); - m_primburst = httpConfig.GetFloat("PrimRequestsBurst", m_primburst); - m_primpersec = httpConfig.GetFloat("PrimRequestsPerSec", m_primpersec); + m_primBurst = httpConfig.GetFloat("PrimRequestsBurst", m_primBurst); + m_primPerSec = httpConfig.GetFloat("PrimRequestsPerSec", m_primPerSec); + m_primOwnerBurst = httpConfig.GetFloat("PrimOwnerRequestsBurst", m_primOwnerBurst); + m_primOwnerPerSec = httpConfig.GetFloat("PrimOwnerRequestsPerSec", m_primOwnerPerSec); + m_httpTimeout = httpConfig.GetInt("RequestsTimeOut", m_httpTimeout); + if(m_httpTimeout > 60000) + m_httpTimeout = 60000; + else if(m_httpTimeout < 200) + m_httpTimeout = 200; } m_pendingRequests = new Dictionary(); m_CompletedRequests = new ConcurrentQueue(); m_RequestsThrottle = new ConcurrentDictionary(); + m_OwnerRequestsThrottle = new ConcurrentDictionary(); // First instance sets this up for all sims if (ThreadPool == null) @@ -405,16 +425,12 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest public void AddRegion(Scene scene) { - m_scene = scene; - - m_scene.RegisterModuleInterface(this); + scene.RegisterModuleInterface(this); } public void RemoveRegion(Scene scene) { scene.UnregisterModuleInterface(this); - if (scene == m_scene) - m_scene = null; } public void PostInitialise() @@ -570,6 +586,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest Request.AllowAutoRedirect = false; Request.KeepAlive = false; + Request.Timeout = HttpTimeout; //This works around some buggy HTTP Servers like Lighttpd Request.ServicePoint.Expect100Continue = false; @@ -629,7 +646,6 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest bstream.Write(data, 0, data.Length); } - Request.Timeout = HttpTimeout; try { // execute the request diff --git a/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs b/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs index 3ab1f6ccca..2c75844fea 100644 --- a/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs +++ b/OpenSim/Region/Framework/Interfaces/IHttpRequests.cs @@ -87,6 +87,6 @@ namespace OpenSim.Region.Framework.Interfaces void StopHttpRequest(uint m_localID, UUID m_itemID); IServiceRequest GetNextCompletedRequest(); void RemoveCompletedRequest(UUID id); - bool CheckThrottle(uint localID); + bool CheckThrottle(uint localID, UUID onerID); } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 6cea821e0a..bb4bab0b58 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -13950,7 +13950,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if(httpScriptMod == null) return ""; - if(!httpScriptMod.CheckThrottle(m_host.LocalId)) + if(!httpScriptMod.CheckThrottle(m_host.LocalId, m_host.OwnerID)) return UUID.Zero.ToString(); List param = new List(); From 4dd89ce094a9b1d5e7c4de684278ed6faeeef58d Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sun, 24 Feb 2019 10:07:48 +0000 Subject: [PATCH 24/62] make the options visible on OpenSimDefaults --- bin/OpenSimDefaults.ini | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 3a80dbedd9..bf271ea1ea 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -661,6 +661,25 @@ ; many simultaneous requests, default is 30 and is currently applied only to assets ;MaxRequestConcurrency = 30 +[ScriptsHttpRequestModule] + ; options for llHttpRequest + + ; max number of concurrent connections per instance (all scenes), default 8 + ; MaxPoolThreads = 8 + + ; max requests per second for all scripts on a prim, default 1 + ;PrimRequestsPerSec = 1.0 + ; initial unthrottled burst for all scripts on a prim, default 3 + ;PrimRequestsBurst = 3.0 + + ; max requests per second for the objects owner (per instance), default 25 + ;PrimOwnerRequestsPerSec = 25.0 + ; initial unthrottled burst for the objects owner (per instance), default 5 + ;PrimOwnerRequestsBurst = 5.0 + + ; requests timeout in miliseconds, range 200 to 60000, default 30000 + ;RequestsTimeOut = 30000 + [AccessControl] ; Viewer-based access control. |-separated list of allowed viewers. ; AllowedClients = "" From d01165818dad7bd81aed07fa983951fecc5a80cd Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Mon, 25 Feb 2019 21:46:23 +0000 Subject: [PATCH 25/62] change UDPPacketBuffer pools (does waste a bit of memory) --- .../ClientStack/Linden/UDP/LLClientView.cs | 8 - .../ClientStack/Linden/UDP/LLUDPClient.cs | 23 +- .../ClientStack/Linden/UDP/LLUDPServer.cs | 212 ++++-------------- .../Linden/UDP/LLUDPServerCommands.cs | 37 --- .../ClientStack/Linden/UDP/OpenSimUDPBase.cs | 199 ++++++++-------- .../Linden/UDP/UnackedPacketCollection.cs | 2 + 6 files changed, 157 insertions(+), 324 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 943be07d2d..3cb9388db1 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -12555,14 +12555,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// provide your own method. protected void OutPacket(Packet packet, ThrottleOutPacketType throttlePacketType, bool doAutomaticSplitting, UnackedPacketMethod method) { - -/* this is causing packet loss for some reason - if(!m_udpClient.IsConnected) - { - PacketPool.Instance.ReturnPacket(packet); - return; - } -*/ if (m_outPacketsToDrop != null) { if (m_outPacketsToDrop.Contains(packet.Type.ToString())) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index 439621ae4e..bc75d820ff 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs @@ -120,7 +120,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// Circuit code that this client is connected on public readonly uint CircuitCode; /// Sequence numbers of packets we've received (for duplicate checking) - public IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(200); + public IncomingPacketHistoryCollection PacketArchive = new IncomingPacketHistoryCollection(256); /// Packets we have sent that need to be ACKed by the client public UnackedPacketCollection NeedAcks = new UnackedPacketCollection(); @@ -803,8 +803,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - - /// /// Fires the OnQueueEmpty callback and sets the minimum time that it /// can be called again @@ -843,6 +841,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP return 0; } + public void FreeUDPBuffer(UDPPacketBuffer buf) + { + m_udpServer.FreeUDPBuffer(buf); + } + /// /// Converts a integer to a /// flag value @@ -853,20 +856,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { ThrottleOutPacketType category = (ThrottleOutPacketType)i; - /* - * Land = 1, - /// Wind data - Wind = 2, - /// Cloud data - Cloud = 3, - /// Any packets that do not fit into the other throttles - Task = 4, - /// Texture assets - Texture = 5, - /// Non-texture assets - Asset = 6, - */ - switch (category) { case ThrottleOutPacketType.Land: diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 35d29a5adb..4739ae8e42 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -344,18 +344,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected ExpiringCache> m_pendingCache = new ExpiringCache>(); - protected Pool m_incomingPacketPool; - - /// - /// Stat for number of packets in the main pool awaiting use. - /// - protected Stat m_poolCountStat; - - /// - /// Stat for number of packets in the inbound packet pool awaiting use. - /// - protected Stat m_incomingPacketPoolStat; - protected int m_defaultRTO = 0; protected int m_maxRTO = 0; protected int m_ackTimeout = 0; @@ -498,7 +486,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // if (usePools) // EnablePools(); - base.DisablePools(); } public void Start() @@ -554,83 +541,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP OqrEngine.Stop(); } - public override bool EnablePools() - { - if (!UsePools) - { - base.EnablePools(); - - m_incomingPacketPool = new Pool(() => new IncomingPacket(), 500); - - return true; - } - - return false; - } - - public override bool DisablePools() - { - if (UsePools) - { - base.DisablePools(); - - StatsManager.DeregisterStat(m_incomingPacketPoolStat); - - // We won't null out the pool to avoid a race condition with code that may be in the middle of using it. - - return true; - } - - return false; - } - - /// - /// This is a seperate method so that it can be called once we have an m_scene to distinguish different scene - /// stats. - /// - protected internal void EnablePoolStats() - { - m_poolCountStat - = new Stat( - "UDPPacketBufferPoolCount", - "Objects within the UDPPacketBuffer pool", - "The number of objects currently stored within the UDPPacketBuffer pool", - "", - "clientstack", - Scene.Name, - StatType.Pull, - stat => stat.Value = Pool.Count, - StatVerbosity.Debug); - - StatsManager.RegisterStat(m_poolCountStat); - - m_incomingPacketPoolStat - = new Stat( - "IncomingPacketPoolCount", - "Objects within incoming packet pool", - "The number of objects currently stored within the incoming packet pool", - "", - "clientstack", - Scene.Name, - StatType.Pull, - stat => stat.Value = m_incomingPacketPool.Count, - StatVerbosity.Debug); - - StatsManager.RegisterStat(m_incomingPacketPoolStat); - } - - /// - /// Disables pool stats. - /// - protected internal void DisablePoolStats() - { - StatsManager.DeregisterStat(m_poolCountStat); - m_poolCountStat = null; - - StatsManager.DeregisterStat(m_incomingPacketPoolStat); - m_incomingPacketPoolStat = null; - } - /// /// If the outgoing UDP thread times out, then return client that was being processed to help with debugging. /// @@ -658,8 +568,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP string.Format("Incoming Packet Async Handling Engine ({0})", Scene.Name), "INCOMING PACKET ASYNC HANDLING ENGINE"); */ - OqrEngine - = new JobEngine( + OqrEngine = new JobEngine( string.Format("Outgoing Queue Refill Engine ({0})", Scene.Name), "OUTGOING QUEUE REFILL ENGINE"); @@ -769,15 +678,20 @@ namespace OpenSim.Region.ClientStack.LindenUDP stat => stat.Value = OqrEngine.JobsWaiting, StatVerbosity.Debug)); - // We delay enabling pool stats to AddScene() instead of Initialize() so that we can distinguish pool stats by - // scene name - if (UsePools) - EnablePoolStats(); - + StatsManager.RegisterStat( + new Stat( + "UDPBuffersPoolCount", + "Buffers in the UDP buffers pool", + "The number of buffers currently stored within the UDP buffers pool", + "", + "clientstack", + Scene.Name, + StatType.Pull, + stat => stat.Value = m_udpBuffersPoolPtr, + StatVerbosity.Debug)); LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this); commands.Register(); - } public bool HandlesRegion(Location x) @@ -939,9 +853,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // The vast majority of packets are less than 200 bytes, although due to asset transfers and packet splitting // there are a decent number of packets in the 1000-1140 byte range. We allocate one of two sizes of data here // to accomodate for both common scenarios and provide ample room for ACK appending in both - int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; + //int bufferSize = (dataLength > 180) ? LLUDPServer.MTU : 200; - UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); + //UDPPacketBuffer buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, bufferSize); + UDPPacketBuffer buffer = GetNewUDPBuffer(udpClient.RemoteEndPoint); // Zerocode if needed if (doZerocode) @@ -971,7 +886,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP // If the packet data wasn't already copied during zerocoding, copy it now if (doCopy) { - if (dataLength <= buffer.Data.Length) + //if (dataLength <= buffer.Data.Length) + if (dataLength <= LLUDPServer.MTU) { Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } @@ -979,7 +895,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_log.Error("[LLUDPSERVER]: Packet exceeded buffer size! This could be an indication of packet assembly not obeying the MTU. Type=" + type + ", DataLength=" + dataLength + ", BufferLength=" + buffer.Data.Length); - buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, dataLength); + // buffer = new UDPPacketBuffer(udpClient.RemoteEndPoint, dataLength); + buffer = GetNewUDPBuffer(udpClient.RemoteEndPoint); Buffer.BlockCopy(data, 0, buffer.Data, 0, dataLength); } } @@ -1168,22 +1085,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Set the appended ACKs flag on this packet buffer.Data[0] = (byte)(buffer.Data[0] | Helpers.MSG_APPENDED_ACKS); } + buffer.DataLength = dataLength; } - buffer.DataLength = dataLength; - if (!isResend) { // Not a resend, assign a new sequence number uint sequenceNumber = (uint)Interlocked.Increment(ref udpClient.CurrentSequence); Utils.UIntToBytesBig(sequenceNumber, buffer.Data, 1); outgoingPacket.SequenceNumber = sequenceNumber; - - if (isReliable) - { - // Add this packet to the list of ACK responses we are waiting on from the server - udpClient.NeedAcks.Add(outgoingPacket); - } } else { @@ -1196,9 +1106,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP PacketsSentCount++; SyncSend(buffer); + // Keep track of when this packet was sent out (right now) outgoingPacket.TickCount = Environment.TickCount & Int32.MaxValue; + if (outgoingPacket.UnackedMethod == null) + FreeUDPBuffer(buffer); + else if(!isResend) + { + // Add this packet to the list of ACK responses we are waiting on from the server + udpClient.NeedAcks.Add(outgoingPacket); + } + if (udpClient.DebugDataOutLevel > 0) m_log.DebugFormat( "[LLUDPSERVER]: Sending packet #{0} (rel: {1}, res: {2}) to {3} from {4}", @@ -1240,7 +1159,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // buffer.DataLength, buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); RecordMalformedInboundPacket(endPoint); - + FreeUDPBuffer(buffer); return; // Drop undersized packet } @@ -1260,21 +1179,21 @@ namespace OpenSim.Region.ClientStack.LindenUDP // buffer.RemoteEndPoint, m_scene.RegionInfo.RegionName); RecordMalformedInboundPacket(endPoint); - + FreeUDPBuffer(buffer); return; // Malformed header } try { -// packet = Packet.BuildPacket(buffer.Data, ref packetEnd, -// // Only allocate a buffer for zerodecoding if the packet is zerocoded -// ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); + packet = Packet.BuildPacket(buffer.Data, ref packetEnd, + // Only allocate a buffer for zerodecoding if the packet is zerocoded + ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all // bytes are copied out). - packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, +// packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, // Only allocate a buffer for zerodecoding if the packet is zerocoded - ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); +// ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); } catch (Exception e) { @@ -1292,7 +1211,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } RecordMalformedInboundPacket(endPoint); - + FreeUDPBuffer(buffer); return; } @@ -1311,17 +1230,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP lock (m_pendingCache) { if (m_pendingCache.Contains(endPoint)) + { + FreeUDPBuffer(buffer); return; + } m_pendingCache.AddOrUpdate(endPoint, new Queue(), 60); } - // We need to copy the endpoint so that it doesn't get changed when another thread reuses the - // buffer. - object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; - - Util.FireAndForget(HandleUseCircuitCode, array); - + Util.FireAndForget(HandleUseCircuitCode, new object[] { endPoint, packet }); + FreeUDPBuffer(buffer); return; } } @@ -1336,24 +1254,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP queue.Enqueue(buffer); return; } - -/* - else if (packet.Type == PacketType.CompleteAgentMovement) - { - // Send ack straight away to let the viewer know that we got it. - SendAckImmediate(endPoint, packet.Header.Sequence); - - // We need to copy the endpoint so that it doesn't get changed when another thread reuses the - // buffer. - object[] array = new object[] { new IPEndPoint(endPoint.Address, endPoint.Port), packet }; - - Util.FireAndForget(HandleCompleteMovementIntoRegion, array); - - return; - } - */ } + FreeUDPBuffer(buffer); + // Determine which agent this packet came from if (client == null || !(client is LLClientView)) { @@ -1471,10 +1375,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP LogPacketHeader(true, udpClient.CircuitCode, 0, packet.Type, (ushort)packet.Length); #endregion BinaryStats - -//AgentUpdate removed from here - - #region Ping Check Handling if (packet.Type == PacketType.StartPingCheck) @@ -1506,17 +1406,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP IncomingPacket incomingPacket; - // Inbox insertion - if (UsePools) - { - incomingPacket = m_incomingPacketPool.GetObject(); - incomingPacket.Client = (LLClientView)client; - incomingPacket.Packet = packet; - } - else - { - incomingPacket = new IncomingPacket((LLClientView)client, packet); - } + incomingPacket = new IncomingPacket((LLClientView)client, packet); // if (incomingPacket.Packet.Type == PacketType.AgentUpdate || // incomingPacket.Packet.Type == PacketType.ChatFromViewer) @@ -1525,7 +1415,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // else // packetInbox.Enqueue(incomingPacket); packetInbox.Add(incomingPacket); - } #region BinaryStats @@ -1881,13 +1770,14 @@ namespace OpenSim.Region.ClientStack.LindenUDP byte[] packetData = ack.ToBytes(); int length = packetData.Length; - UDPPacketBuffer buffer = new UDPPacketBuffer(remoteEndpoint, length); + UDPPacketBuffer buffer = GetNewUDPBuffer(remoteEndpoint); buffer.DataLength = length; Buffer.BlockCopy(packetData, 0, buffer.Data, 0, length); // AsyncBeginSend(buffer); SyncSend(buffer); + FreeUDPBuffer(buffer); } protected bool IsClientAuthorized(UseCircuitCodePacket useCircuitCode, out AuthenticateResponse sessionInfo) @@ -1982,17 +1872,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP Scene.ThreadAlive(1); try { - packetInbox.TryTake(out incomingPacket, 250); + packetInbox.TryTake(out incomingPacket, 4500); if (incomingPacket != null && IsRunningInbound) { ProcessInPacket(incomingPacket); - - if (UsePools) - { - incomingPacket.Client = null; - m_incomingPacketPool.ReturnObject(incomingPacket); - } incomingPacket = null; } } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs index 012a57d969..a4d7eb924e 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServerCommands.cs @@ -777,41 +777,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_udpServer.StopOutbound(); } - private void HandlePoolCommand(string module, string[] args) - { - if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) - return; - - if (args.Length != 4) - { - MainConsole.Instance.Output("Usage: debug lludp pool "); - return; - } - - string enabled = args[3]; - - if (enabled == "on") - { - if (m_udpServer.EnablePools()) - { - m_udpServer.EnablePoolStats(); - MainConsole.Instance.OutputFormat("Packet pools enabled on {0}", m_udpServer.Scene.Name); - } - } - else if (enabled == "off") - { - if (m_udpServer.DisablePools()) - { - m_udpServer.DisablePoolStats(); - MainConsole.Instance.OutputFormat("Packet pools disabled on {0}", m_udpServer.Scene.Name); - } - } - else - { - MainConsole.Instance.Output("Usage: debug lludp pool "); - } - } - private void HandleAgentUpdateCommand(string module, string[] args) { if (SceneManager.Instance.CurrentScene != null && SceneManager.Instance.CurrentScene != m_udpServer.Scene) @@ -834,8 +799,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP MainConsole.Instance.OutputFormat( "OUT LLUDP packet processing for {0} is {1}", m_udpServer.Scene.Name, m_udpServer.IsRunningOutbound ? "enabled" : "disabled"); - MainConsole.Instance.OutputFormat("LLUDP pools in {0} are {1}", m_udpServer.Scene.Name, m_udpServer.UsePools ? "on" : "off"); - MainConsole.Instance.OutputFormat( "Packet debug level for new clients is {0}", m_udpServer.DefaultClientPacketDebugLevel); } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index f362b06f0a..0bfd86cdef 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs @@ -26,6 +26,7 @@ */ using System; +using System.Collections.Concurrent; using System.Net; using System.Net.Sockets; using System.Threading; @@ -57,15 +58,9 @@ namespace OpenMetaverse /// UDP socket, used in either client or server mode private Socket m_udpSocket; - /// - /// Are we to use object pool(s) to reduce memory churn when receiving data? - /// - public bool UsePools { get; protected set; } - - /// - /// Pool to use for handling data. May be null if UsePools = false; - /// - protected OpenSim.Framework.Pool Pool { get; private set; } + public static Object m_udpBuffersPoolLock = new Object(); + public static UDPPacketBuffer[] m_udpBuffersPool = new UDPPacketBuffer[1000]; + public static int m_udpBuffersPoolPtr = -1; /// Returns true if the server is currently listening for inbound packets, otherwise false public bool IsRunningInbound { get; private set; } @@ -186,6 +181,52 @@ namespace OpenMetaverse if(m_udpSocket !=null) try { m_udpSocket.Close(); } catch { } } + + public UDPPacketBuffer GetNewUDPBuffer() + { + lock (m_udpBuffersPoolLock) + { + if (m_udpBuffersPoolPtr >= 0) + { + UDPPacketBuffer buf = m_udpBuffersPool[m_udpBuffersPoolPtr]; + m_udpBuffersPool[m_udpBuffersPoolPtr] = null; + m_udpBuffersPoolPtr--; + buf.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); + return buf; + } + } + return new UDPPacketBuffer(new IPEndPoint(IPAddress.Any, 0)); + } + + public UDPPacketBuffer GetNewUDPBuffer(IPEndPoint remoteEndpoint) + { + lock (m_udpBuffersPoolLock) + { + if (m_udpBuffersPoolPtr >= 0) + { + UDPPacketBuffer buf = m_udpBuffersPool[m_udpBuffersPoolPtr]; + m_udpBuffersPool[m_udpBuffersPoolPtr] = null; + m_udpBuffersPoolPtr--; + buf.RemoteEndPoint = remoteEndpoint; + return buf; + } + } + return new UDPPacketBuffer(remoteEndpoint); + } + + public void FreeUDPBuffer(UDPPacketBuffer buf) + { + lock (m_udpBuffersPoolLock) + { + if (m_udpBuffersPoolPtr < 999) + { + buf.RemoteEndPoint = null; + m_udpBuffersPoolPtr++; + m_udpBuffersPool[m_udpBuffersPoolPtr] = buf; + } + } + } + /// /// Start inbound UDP packet handling. /// @@ -202,6 +243,7 @@ namespace OpenMetaverse /// manner (not throwing an exception when the remote side resets the /// connection). This call is ignored on Mono where the flag is not /// necessary + public virtual void StartInbound(int recvBufferSize) { if (!IsRunningInbound) @@ -306,101 +348,64 @@ namespace OpenMetaverse IsRunningOutbound = false; } - public virtual bool EnablePools() - { - if (!UsePools) - { - Pool = new Pool(() => new UDPPacketBuffer(), 500); - - UsePools = true; - - return true; - } - - return false; - } - - public virtual bool DisablePools() - { - if (UsePools) - { - UsePools = false; - - // We won't null out the pool to avoid a race condition with code that may be in the middle of using it. - - return true; - } - - return false; - } - private void AsyncBeginReceive() { - UDPPacketBuffer buf; + if (!IsRunningInbound) + return; - // FIXME: Disabled for now as this causes issues with reused packet objects interfering with each other - // on Windows with m_asyncPacketHandling = true, though this has not been seen on Linux. - // Possibly some unexpected issue with fetching UDP data concurrently with multiple threads. Requires more investigation. -// if (UsePools) -// buf = Pool.GetObject(); -// else - buf = new UDPPacketBuffer(); - - if (IsRunningInbound) + UDPPacketBuffer buf = GetNewUDPBuffer(); + try { - try + // kick off an async read + m_udpSocket.BeginReceiveFrom( + //wrappedBuffer.Instance.Data, + buf.Data, + 0, + UDPPacketBuffer.BUFFER_SIZE, + SocketFlags.None, + ref buf.RemoteEndPoint, + AsyncEndReceive, + //wrappedBuffer); + buf); + } + catch (SocketException e) + { + if (e.SocketErrorCode == SocketError.ConnectionReset) { - // kick off an async read - m_udpSocket.BeginReceiveFrom( - //wrappedBuffer.Instance.Data, - buf.Data, - 0, - UDPPacketBuffer.BUFFER_SIZE, - SocketFlags.None, - ref buf.RemoteEndPoint, - AsyncEndReceive, - //wrappedBuffer); - buf); - } - catch (SocketException e) - { - if (e.SocketErrorCode == SocketError.ConnectionReset) + m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); + bool salvaged = false; + while (!salvaged) { - m_log.Warn("[UDPBASE]: SIO_UDP_CONNRESET was ignored, attempting to salvage the UDP listener on port " + m_udpPort); - bool salvaged = false; - while (!salvaged) + try { - try - { - m_udpSocket.BeginReceiveFrom( - //wrappedBuffer.Instance.Data, - buf.Data, - 0, - UDPPacketBuffer.BUFFER_SIZE, - SocketFlags.None, - ref buf.RemoteEndPoint, - AsyncEndReceive, - //wrappedBuffer); - buf); - salvaged = true; - } - catch (SocketException) { } - catch (ObjectDisposedException) { return; } + m_udpSocket.BeginReceiveFrom( + //wrappedBuffer.Instance.Data, + buf.Data, + 0, + UDPPacketBuffer.BUFFER_SIZE, + SocketFlags.None, + ref buf.RemoteEndPoint, + AsyncEndReceive, + //wrappedBuffer); + buf); + salvaged = true; } - - m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); + catch (SocketException) { } + catch (ObjectDisposedException) { return; } } + + m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); } - catch (ObjectDisposedException e) - { - m_log.Error( - string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); - } - catch (Exception e) - { - m_log.Error( - string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); - } + } + catch (ObjectDisposedException e) + { + m_log.Error( + string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); + } + catch (Exception e) + { + m_log.Error( + string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); } } @@ -465,14 +470,12 @@ namespace OpenMetaverse } finally { -// if (UsePools) -// Pool.ReturnObject(buffer); - AsyncBeginReceive(); } } } +/* not in use public void AsyncBeginSend(UDPPacketBuffer buf) { // if (IsRunningOutbound) @@ -511,7 +514,7 @@ namespace OpenMetaverse catch (SocketException) { } catch (ObjectDisposedException) { } } - +*/ public void SyncSend(UDPPacketBuffer buf) { try diff --git a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs index 76f4c6f86e..e0eee53f8b 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs @@ -203,6 +203,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (ackedPacket != null) { m_packets.Remove(pendingAcknowledgement.SequenceNumber); + ackedPacket.Client.FreeUDPBuffer(ackedPacket.Buffer); // As with other network applications, assume that an acknowledged packet is an // indication that the network can handle a little more load, speed up the transmission @@ -241,6 +242,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (removedPacket != null) { m_packets.Remove(pendingRemove); + removedPacket.Client.FreeUDPBuffer(removedPacket.Buffer); // Update stats Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); From 91fab702361ecaf8a442e7cf3a469aaa6daead44 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Mon, 25 Feb 2019 23:05:11 +0000 Subject: [PATCH 26/62] removed a redundant function; try to make a particular vi coder happy about removed comments --- .../ClientStack/Linden/UDP/LLUDPClient.cs | 12 ++++++------ .../ClientStack/Linden/UDP/OpenSimUDPBase.cs | 18 +----------------- 2 files changed, 7 insertions(+), 23 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index bc75d820ff..e0cca05219 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs @@ -859,17 +859,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP switch (category) { case ThrottleOutPacketType.Land: - return ThrottleOutPacketTypeFlags.Land; + return ThrottleOutPacketTypeFlags.Land; // Terrain data case ThrottleOutPacketType.Wind: - return ThrottleOutPacketTypeFlags.Wind; + return ThrottleOutPacketTypeFlags.Wind; // Wind data case ThrottleOutPacketType.Cloud: - return ThrottleOutPacketTypeFlags.Cloud; + return ThrottleOutPacketTypeFlags.Cloud; // Cloud data case ThrottleOutPacketType.Task: - return ThrottleOutPacketTypeFlags.Task; + return ThrottleOutPacketTypeFlags.Task; // Object updates and everything not on the other categories case ThrottleOutPacketType.Texture: - return ThrottleOutPacketTypeFlags.Texture; + return ThrottleOutPacketTypeFlags.Texture; // Textures data (also impacts http texture and mesh by default) case ThrottleOutPacketType.Asset: - return ThrottleOutPacketTypeFlags.Asset; + return ThrottleOutPacketTypeFlags.Asset; // Non-texture Assets data default: return 0; } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index 0bfd86cdef..7f6a292dd0 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs @@ -182,22 +182,6 @@ namespace OpenMetaverse try { m_udpSocket.Close(); } catch { } } - public UDPPacketBuffer GetNewUDPBuffer() - { - lock (m_udpBuffersPoolLock) - { - if (m_udpBuffersPoolPtr >= 0) - { - UDPPacketBuffer buf = m_udpBuffersPool[m_udpBuffersPoolPtr]; - m_udpBuffersPool[m_udpBuffersPoolPtr] = null; - m_udpBuffersPoolPtr--; - buf.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 0); - return buf; - } - } - return new UDPPacketBuffer(new IPEndPoint(IPAddress.Any, 0)); - } - public UDPPacketBuffer GetNewUDPBuffer(IPEndPoint remoteEndpoint) { lock (m_udpBuffersPoolLock) @@ -353,7 +337,7 @@ namespace OpenMetaverse if (!IsRunningInbound) return; - UDPPacketBuffer buf = GetNewUDPBuffer(); + UDPPacketBuffer buf = GetNewUDPBuffer(new IPEndPoint(IPAddress.Any, 0)); // we need a fresh one here, for now at least try { // kick off an async read From d5d4dc475461f889194a84aa682fbd555aad303f Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Mon, 25 Feb 2019 23:30:01 +0000 Subject: [PATCH 27/62] zero decode using a buffer from the pool --- .../ClientStack/Linden/UDP/LLUDPServer.cs | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 4739ae8e42..e931f3b884 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -1185,15 +1185,23 @@ namespace OpenSim.Region.ClientStack.LindenUDP try { - packet = Packet.BuildPacket(buffer.Data, ref packetEnd, - // Only allocate a buffer for zerodecoding if the packet is zerocoded - ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); + // get a buffer for zero decode using the udp buffers pool + UDPPacketBuffer zerodecodebufferholder = null; + byte[] zerodecodebuffer = null; + // only if needed + if (((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0)) + { + zerodecodebufferholder = GetNewUDPBuffer(null); + zerodecodebuffer = zerodecodebufferholder.Data; + } + + packet = Packet.BuildPacket(buffer.Data, ref packetEnd, zerodecodebuffer); // If OpenSimUDPBase.UsePool == true (which is currently separate from the PacketPool) then we // assume that packet construction does not retain a reference to byte[] buffer.Data (instead, all // bytes are copied out). -// packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, - // Only allocate a buffer for zerodecoding if the packet is zerocoded -// ((buffer.Data[0] & Helpers.MSG_ZEROCODED) != 0) ? new byte[4096] : null); + // packet = PacketPool.Instance.GetPacket(buffer.Data, ref packetEnd, zerodecodebuffer); + if(zerodecodebufferholder != null) + FreeUDPBuffer(zerodecodebufferholder); } catch (Exception e) { From a07951b04471ff4d0c6204f2c13c49248f0b7d19 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Tue, 26 Feb 2019 00:43:44 +0000 Subject: [PATCH 28/62] fix UDPBuffersPoolCount value (readable with stats show clientstack --- OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index e931f3b884..ba5a2f3b1e 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -687,7 +687,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP "clientstack", Scene.Name, StatType.Pull, - stat => stat.Value = m_udpBuffersPoolPtr, + stat => stat.Value = m_udpBuffersPoolPtr + 1, StatVerbosity.Debug)); LLUDPServerCommands commands = new LLUDPServerCommands(MainConsole.Instance, this); @@ -1582,7 +1582,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { m_log.DebugFormat("[LLUDPSERVER]: Client created but no pending queue present"); return; - } m_pendingCache.Remove(endPoint); } From 91569e00a0a4f9b9124979b73e00a4e88020c6cd Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Tue, 26 Feb 2019 01:16:30 +0000 Subject: [PATCH 29/62] buffers in pool have no data, better mk that clear --- OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index 7f6a292dd0..5fa46372e3 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs @@ -205,6 +205,7 @@ namespace OpenMetaverse if (m_udpBuffersPoolPtr < 999) { buf.RemoteEndPoint = null; + buf.DataLength = 0; m_udpBuffersPoolPtr++; m_udpBuffersPool[m_udpBuffersPoolPtr] = buf; } From e24adb9ea12d68f743b5936d6b04899dcef83dfc Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Tue, 26 Feb 2019 13:38:03 +0000 Subject: [PATCH 30/62] mantis 6569: restore full health on invulnerable set to true; combat module is outdated and needs work. By then the proposal on this mantis should be reviewed --- OpenSim/Region/Framework/Scenes/ScenePresence.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index cfb1be4f98..f3e217906c 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -523,7 +523,12 @@ namespace OpenSim.Region.Framework.Scenes public bool Invulnerable { - set { m_invulnerable = value; } + set + { + m_invulnerable = value; + if(value && Health != 100.0f) + Health = 100.0f; + } get { return m_invulnerable; } } From 4de5e14e542cd9724625e87622f29fecf5652642 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Tue, 26 Feb 2019 15:02:57 +0000 Subject: [PATCH 31/62] issues with udp buffers pool on heavy load --- .../ClientStack/Linden/UDP/LLUDPClient.cs | 30 ++++++++++++++----- .../ClientStack/Linden/UDP/LLUDPServer.cs | 29 ++++++++++-------- .../ClientStack/Linden/UDP/OpenSimUDPBase.cs | 22 ++++---------- .../Linden/UDP/UnackedPacketCollection.cs | 14 ++++++--- 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index e0cca05219..fca7943c12 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs @@ -650,6 +650,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP // leaving a dequeued packet still waiting to be sent out. Try to // send it again OutgoingPacket nextPacket = m_nextPackets[i]; + if(nextPacket.Buffer == null) + { + if (m_packetOutboxes[i].Count < 5) + emptyCategories |= CategoryToFlag(i); + continue; + } if (bucket.RemoveTokens(nextPacket.Buffer.DataLength)) { // Send the packet @@ -681,21 +687,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // A packet was pulled off the queue. See if we have // enough tokens in the bucket to send it out - if (bucket.RemoveTokens(packet.Buffer.DataLength)) + if(packet.Buffer == null) { - // Send the packet - m_udpServer.SendPacketFinal(packet); - packetSent = true; - + // packet canceled elsewhere (by a ack for example) if (queue.Count < 5) emptyCategories |= CategoryToFlag(i); } else { - // Save the dequeued packet for the next iteration - m_nextPackets[i] = packet; - } + if (bucket.RemoveTokens(packet.Buffer.DataLength)) + { + // Send the packet + m_udpServer.SendPacketFinal(packet); + packetSent = true; + if (queue.Count < 5) + emptyCategories |= CategoryToFlag(i); + } + else + { + // Save the dequeued packet for the next iteration + m_nextPackets[i] = packet; + } + } } else { diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index ba5a2f3b1e..d324623112 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -983,7 +983,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { LLUDPClient udpClient = client.UDPClient; - if (!udpClient.IsConnected) + if (!client.IsActive || !udpClient.IsConnected) return; // Disconnect an agent if no packets are received for some time @@ -1053,14 +1053,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP internal void SendPacketFinal(OutgoingPacket outgoingPacket) { UDPPacketBuffer buffer = outgoingPacket.Buffer; + if(buffer == null) // canceled packet + return; + LLUDPClient udpClient = outgoingPacket.Client; + if (!udpClient.IsConnected) + return; + byte flags = buffer.Data[0]; bool isResend = (flags & Helpers.MSG_RESENT) != 0; bool isReliable = (flags & Helpers.MSG_RELIABLE) != 0; bool isZerocoded = (flags & Helpers.MSG_ZEROCODED) != 0; - LLUDPClient udpClient = outgoingPacket.Client; - - if (!udpClient.IsConnected) - return; int dataLength = buffer.DataLength; @@ -1916,7 +1918,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { Scene.ThreadAlive(2); - try { m_packetSent = false; @@ -1971,7 +1972,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else if (!m_packetSent) // Thread.Sleep((int)TickCountResolution); outch this is bad on linux - Thread.Sleep(15); // match the 16ms of windows7, dont ask 16 or win may decide to do 32ms. + Thread.Sleep(15); // match the 16ms of windows, dont ask 16 or win may decide to do 32ms. Watchdog.UpdateThread(); } @@ -1995,14 +1996,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (udpClient.IsConnected) { - if (m_resendUnacked) + if (client.IsActive && m_resendUnacked) HandleUnacked(llClient); - if (m_sendAcks) - SendAcks(udpClient); + if (client.IsActive) + { + if (m_sendAcks) + SendAcks(udpClient); - if (m_sendPing) - SendPing(udpClient); + if (m_sendPing) + SendPing(udpClient); + } // Dequeue any outgoing packets that are within the throttle limits if (udpClient.DequeueOutgoing()) @@ -2015,7 +2019,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_log.Error( string.Format("[LLUDPSERVER]: OutgoingPacketHandler iteration for {0} threw ", client.Name), ex); } - client = null; } #region Emergency Monitoring diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index 5fa46372e3..6f346d3756 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs @@ -343,14 +343,12 @@ namespace OpenMetaverse { // kick off an async read m_udpSocket.BeginReceiveFrom( - //wrappedBuffer.Instance.Data, buf.Data, 0, - UDPPacketBuffer.BUFFER_SIZE, + buf.Data.Length, SocketFlags.None, ref buf.RemoteEndPoint, AsyncEndReceive, - //wrappedBuffer); buf); } catch (SocketException e) @@ -364,14 +362,12 @@ namespace OpenMetaverse try { m_udpSocket.BeginReceiveFrom( - //wrappedBuffer.Instance.Data, buf.Data, 0, - UDPPacketBuffer.BUFFER_SIZE, + buf.Data.Length, SocketFlags.None, ref buf.RemoteEndPoint, AsyncEndReceive, - //wrappedBuffer); buf); salvaged = true; } @@ -382,11 +378,6 @@ namespace OpenMetaverse m_log.Warn("[UDPBASE]: Salvaged the UDP listener on port " + m_udpPort); } } - catch (ObjectDisposedException e) - { - m_log.Error( - string.Format("[UDPBASE]: Error processing UDP begin receive {0}. Exception ", UdpReceives), e); - } catch (Exception e) { m_log.Error( @@ -443,11 +434,6 @@ namespace OpenMetaverse UdpReceives, se.ErrorCode), se); } - catch (ObjectDisposedException e) - { - m_log.Error( - string.Format("[UDPBASE]: Error processing UDP end receive {0}. Exception ", UdpReceives), e); - } catch (Exception e) { m_log.Error( @@ -502,6 +488,8 @@ namespace OpenMetaverse */ public void SyncSend(UDPPacketBuffer buf) { + if(buf.RemoteEndPoint == null) + return; // was already expired try { m_udpSocket.SendTo( @@ -515,7 +503,7 @@ namespace OpenMetaverse } catch (SocketException e) { - m_log.Warn("[UDPBASE]: sync send SocketException {0} " + e.Message); + m_log.WarnFormat("[UDPBASE]: sync send SocketException {0} {1}", buf.RemoteEndPoint, e.Message); } catch (ObjectDisposedException) { } } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs index e0eee53f8b..1f978e13c3 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/UnackedPacketCollection.cs @@ -189,8 +189,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Process all the pending adds OutgoingPacket pendingAdd; while (m_pendingAdds.TryDequeue(out pendingAdd)) + { if (pendingAdd != null) m_packets[pendingAdd.SequenceNumber] = pendingAdd; + } // Process all the pending removes, including updating statistics and round-trip times PendingAck pendingAcknowledgement; @@ -203,15 +205,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (ackedPacket != null) { m_packets.Remove(pendingAcknowledgement.SequenceNumber); + + // Update stats + Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); + ackedPacket.Client.FreeUDPBuffer(ackedPacket.Buffer); + ackedPacket.Buffer = null; // As with other network applications, assume that an acknowledged packet is an // indication that the network can handle a little more load, speed up the transmission ackedPacket.Client.FlowThrottle.AcknowledgePackets(1); - // Update stats - Interlocked.Add(ref ackedPacket.Client.UnackedBytes, -ackedPacket.Buffer.DataLength); - if (!pendingAcknowledgement.FromResend) { // Calculate the round-trip time for this packet and its ACK @@ -242,10 +246,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (removedPacket != null) { m_packets.Remove(pendingRemove); - removedPacket.Client.FreeUDPBuffer(removedPacket.Buffer); // Update stats Interlocked.Add(ref removedPacket.Client.UnackedBytes, -removedPacket.Buffer.DataLength); + + removedPacket.Client.FreeUDPBuffer(removedPacket.Buffer); + removedPacket.Buffer = null; } } } From fe46f8cd1ddbf3d37e8be4a3771ce5124024d6f8 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Tue, 26 Feb 2019 15:09:18 +0000 Subject: [PATCH 32/62] avoid packet split on terseupdates --- .../ClientStack/Linden/UDP/LLClientView.cs | 106 +++++++++++++----- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 3cb9388db1..f28534b3f8 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -4094,11 +4094,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP return; List objectUpdateBlocks = null; - List compressedUpdateBlocks = null; - List terseUpdateBlocks = null; - List terseAgentUpdateBlocks = null; + // List compressedUpdateBlocks = null; List objectUpdates = null; - List compressedUpdates = null; + // List compressedUpdates = null; List terseUpdates = null; List terseAgentUpdates = null; List ObjectAnimationUpdates = null; @@ -4321,32 +4319,29 @@ namespace OpenSim.Region.ClientStack.LindenUDP if ((updateFlags & canNotUseImprovedMask) == 0) { - ImprovedTerseObjectUpdatePacket.ObjectDataBlock ablock = - CreateImprovedTerseBlock(update.Entity); if (update.Entity is ScenePresence) { // ALL presence updates go into a special list - if (terseAgentUpdateBlocks == null) + if (terseAgentUpdates == null) { - terseAgentUpdateBlocks = new List(); terseAgentUpdates = new List(); + maxUpdatesBytes -= 18; } - terseAgentUpdateBlocks.Add(ablock); terseAgentUpdates.Add(update); + maxUpdatesBytes -= 63; // no texture entry } else { // Everything else goes here - if (terseUpdateBlocks == null) + if (terseUpdates == null) { - terseUpdateBlocks = new List(); terseUpdates = new List(); + maxUpdatesBytes -= 18; } - terseUpdateBlocks.Add(ablock); terseUpdates.Add(update); + maxUpdatesBytes -= 47; // no texture entry } - maxUpdatesBytes -= ablock.Length; } else { @@ -4359,6 +4354,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { objectUpdateBlocks = new List(); objectUpdates = new List(); + maxUpdatesBytes -= 18; } objectUpdateBlocks.Add(ablock); objectUpdates.Add(update); @@ -4377,16 +4373,42 @@ namespace OpenSim.Region.ClientStack.LindenUDP timeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); - if (terseAgentUpdateBlocks != null) + if (terseAgentUpdates != null) { + const int maxNBlocks = (LLUDPServer.MTU - 18) / 63; // no texture entry + int blocks = terseAgentUpdates.Count; + ImprovedTerseObjectUpdatePacket packet - = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); + = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.TimeDilation = timeDilation; - packet.ObjectData = terseAgentUpdateBlocks.ToArray(); - terseAgentUpdateBlocks.Clear(); - OutPacket(packet, ThrottleOutPacketType.Unknown, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseAgentUpdates, oPacket); }); + int curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; + List tau = new List(curNBlocks); + packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[curNBlocks]; + + int count = 0; + foreach (EntityUpdate eu in terseAgentUpdates) + { + packet.ObjectData[count++] = CreateImprovedTerseBlock(eu.Entity); + tau.Add(eu); + --blocks; + if (count == curNBlocks && blocks > 0) + { + OutPacket(packet, ThrottleOutPacketType.Unknown, false, delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }); + packet = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); + packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; + packet.RegionData.TimeDilation = timeDilation; + + curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; + tau = new List(curNBlocks); + packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[curNBlocks]; + count = 0; + } + } + + if (tau.Count > 0) + OutPacket(packet, ThrottleOutPacketType.Unknown, false, delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }); } if (objectUpdateBlocks != null) @@ -4399,7 +4421,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(objectUpdates, oPacket); }); } - +/* if (compressedUpdateBlocks != null) { ObjectUpdateCompressedPacket packet = (ObjectUpdateCompressedPacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdateCompressed); @@ -4410,20 +4432,46 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(compressedUpdates, oPacket); }); } - - if (terseUpdateBlocks != null) +*/ + if (terseUpdates != null) { - ImprovedTerseObjectUpdatePacket packet = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket( - PacketType.ImprovedTerseObjectUpdate); + const int maxNBlocks = (LLUDPServer.MTU - 18) / 47; // no texture entry + int blocks = terseUpdates.Count; + + ImprovedTerseObjectUpdatePacket packet = + (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; packet.RegionData.TimeDilation = timeDilation; - packet.ObjectData = terseUpdateBlocks.ToArray(); - terseUpdateBlocks.Clear(); - OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(terseUpdates, oPacket); }); + int curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; + List tau = new List(curNBlocks); + packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[curNBlocks]; + + int count = 0; + foreach (EntityUpdate eu in terseUpdates) + { + packet.ObjectData[count++] = CreateImprovedTerseBlock(eu.Entity); + tau.Add(eu); + --blocks; + if (count == curNBlocks && blocks > 0) + { + OutPacket(packet, ThrottleOutPacketType.Task, false, delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }); + packet = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); + packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; + packet.RegionData.TimeDilation = timeDilation; + + curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; + tau = new List(curNBlocks); + packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[curNBlocks]; + count = 0; + } + } + + if (tau.Count > 0) + OutPacket(packet, ThrottleOutPacketType.Task, false, delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }); } - if(ObjectAnimationUpdates != null) + if (ObjectAnimationUpdates != null) { foreach (SceneObjectPart sop in ObjectAnimationUpdates) { @@ -4637,9 +4685,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // These are used to implement an adaptive backoff in the number // of updates converted to packets. Since we don't want packets // to sit in the queue with old data, only convert enough updates - // to packets that can be sent in 200ms. -// private Int32 m_LastQueueFill = 0; -// private Int32 m_maxUpdates = 0; + // to packets that can be sent in 30ms. void HandleQueueEmpty(ThrottleOutPacketTypeFlags categories) { From bcf05afd64d3b38c66d6d117a51e336a7e98dfc3 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 27 Feb 2019 10:07:25 +0000 Subject: [PATCH 33/62] direct encode terseupdates --- .../ClientStack/Linden/UDP/LLClientView.cs | 210 +++++++++++++++--- .../ClientStack/Linden/UDP/LLUDPClient.cs | 11 +- .../ClientStack/Linden/UDP/LLUDPServer.cs | 21 ++ .../ClientStack/Linden/UDP/OpenSimUDPBase.cs | 2 +- 4 files changed, 207 insertions(+), 37 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index f28534b3f8..a9edf081de 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -4084,6 +4084,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP ResendPrimUpdate(update); } + static private readonly byte[] terseUpdateHeader = new byte[] { + Helpers.MSG_RELIABLE, + 0, 0, 0, 0, // sequence number + 0, // extra + 15 // ID (high frequency) + }; + private void ProcessEntityUpdates(int maxUpdatesBytes) { if (!IsActive) @@ -4377,38 +4384,51 @@ namespace OpenSim.Region.ClientStack.LindenUDP { const int maxNBlocks = (LLUDPServer.MTU - 18) / 63; // no texture entry int blocks = terseAgentUpdates.Count; - - ImprovedTerseObjectUpdatePacket packet - = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); - packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; - packet.RegionData.TimeDilation = timeDilation; - int curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; List tau = new List(curNBlocks); - packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[curNBlocks]; + + UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + + //setup header and regioninfo block + Array.Copy(terseUpdateHeader, buf.Data, 7); + Utils.UInt64ToBytesSafepos(m_scene.RegionInfo.RegionHandle, buf.Data, 7); + Utils.UInt16ToBytes(timeDilation, buf.Data, 15); + buf.Data[17] = (byte)curNBlocks; + int pos = 18; int count = 0; foreach (EntityUpdate eu in terseAgentUpdates) { - packet.ObjectData[count++] = CreateImprovedTerseBlock(eu.Entity); + CreateImprovedTerseBlock(eu.Entity, buf.Data, ref pos); tau.Add(eu); + ++count; --blocks; if (count == curNBlocks && blocks > 0) { - OutPacket(packet, ThrottleOutPacketType.Unknown, false, delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }); - packet = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); - packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; - packet.RegionData.TimeDilation = timeDilation; + // we need more packets + UDPPacketBuffer newbuf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + Array.Copy(buf.Data, newbuf.Data, 17); // start is the same + + buf.DataLength = pos; + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Unknown, + delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false); curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; tau = new List(curNBlocks); - packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[curNBlocks]; count = 0; + + buf = newbuf; + buf.Data[17] = (byte)curNBlocks; + pos = 18; } } - if (tau.Count > 0) - OutPacket(packet, ThrottleOutPacketType.Unknown, false, delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }); + if (count > 0) + { + buf.DataLength = pos; + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Unknown, + delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false); + } } if (objectUpdateBlocks != null) @@ -4437,38 +4457,51 @@ namespace OpenSim.Region.ClientStack.LindenUDP { const int maxNBlocks = (LLUDPServer.MTU - 18) / 47; // no texture entry int blocks = terseUpdates.Count; - - ImprovedTerseObjectUpdatePacket packet = - (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); - packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; - packet.RegionData.TimeDilation = timeDilation; - int curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; List tau = new List(curNBlocks); - packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[curNBlocks]; + + UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + + //setup header and regioninfo block + Array.Copy(terseUpdateHeader, buf.Data, 7); + Utils.UInt64ToBytesSafepos(m_scene.RegionInfo.RegionHandle, buf.Data, 7); + Utils.UInt16ToBytes(timeDilation, buf.Data, 15); + buf.Data[17] = (byte)curNBlocks; + int pos = 18; int count = 0; foreach (EntityUpdate eu in terseUpdates) { - packet.ObjectData[count++] = CreateImprovedTerseBlock(eu.Entity); + CreateImprovedTerseBlock(eu.Entity, buf.Data, ref pos); tau.Add(eu); + ++count; --blocks; if (count == curNBlocks && blocks > 0) { - OutPacket(packet, ThrottleOutPacketType.Task, false, delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }); - packet = (ImprovedTerseObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ImprovedTerseObjectUpdate); - packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; - packet.RegionData.TimeDilation = timeDilation; + // we need more packets + UDPPacketBuffer newbuf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + Array.Copy(buf.Data, newbuf.Data, 17); // start is the same + + buf.DataLength = pos; + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, + delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false); curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; tau = new List(curNBlocks); - packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[curNBlocks]; count = 0; + + buf = newbuf; + buf.Data[17] = (byte)curNBlocks; + pos = 18; } } - if (tau.Count > 0) - OutPacket(packet, ThrottleOutPacketType.Task, false, delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }); + if (count > 0) + { + buf.DataLength = pos; + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, + delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false); + } } if (ObjectAnimationUpdates != null) @@ -5686,6 +5719,123 @@ namespace OpenSim.Region.ClientStack.LindenUDP return block; } + + protected void CreateImprovedTerseBlock(ISceneEntity entity, byte[] data, ref int pos) + { + #region ScenePresence/SOP Handling + + bool avatar = (entity is ScenePresence); + uint localID = entity.LocalId; + uint attachPoint; + Vector4 collisionPlane; + Vector3 position, velocity, acceleration, angularVelocity; + Quaternion rotation; + byte datasize; + + if (avatar) + { + ScenePresence presence = (ScenePresence)entity; + + position = presence.OffsetPosition; + velocity = presence.Velocity; + acceleration = Vector3.Zero; + rotation = presence.Rotation; + // tpvs can only see rotations around Z in some cases + if (!presence.Flying && !presence.IsSatOnObject) + { + rotation.X = 0f; + rotation.Y = 0f; + } + rotation.Normalize(); + angularVelocity = presence.AngularVelocity; + + // m_log.DebugFormat( + // "[LLCLIENTVIEW]: Sending terse update to {0} with position {1} in {2}", Name, presence.OffsetPosition, m_scene.Name); + + attachPoint = presence.State; + collisionPlane = presence.CollisionPlane; + + datasize = 60; + } + else + { + SceneObjectPart part = (SceneObjectPart)entity; + + attachPoint = part.ParentGroup.AttachmentPoint; + attachPoint = ((attachPoint % 16) * 16 + (attachPoint / 16)); + // m_log.DebugFormat( + // "[LLCLIENTVIEW]: Sending attachPoint {0} for {1} {2} to {3}", + // attachPoint, part.Name, part.LocalId, Name); + + collisionPlane = Vector4.Zero; + position = part.RelativePosition; + velocity = part.Velocity; + acceleration = part.Acceleration; + angularVelocity = part.AngularVelocity; + rotation = part.RotationOffset; + + datasize = 44; + } + + #endregion ScenePresence/SOP Handling + //object block size + data[pos++] = datasize; + + // LocalID + Utils.UIntToBytes(localID, data, pos); + pos += 4; + + // Avatar/CollisionPlane + data[pos++] = (byte)attachPoint; + if (avatar) + { + data[pos++] = 1; + + if (collisionPlane == Vector4.Zero) + collisionPlane = Vector4.UnitW; + //m_log.DebugFormat("CollisionPlane: {0}",collisionPlane); + collisionPlane.ToBytes(data, pos); + pos += 16; + } + else + { + data[pos++] = 0; + } + + // Position + position.ToBytes(data, pos); + pos += 12; + + // Velocity + ClampVectorForUint(ref velocity, 128f); + Utils.FloatToUInt16Bytes(velocity.X, 128.0f, data, pos); pos += 2; + Utils.FloatToUInt16Bytes(velocity.Y, 128.0f, data, pos); pos += 2; + Utils.FloatToUInt16Bytes(velocity.Z, 128.0f, data, pos); pos += 2; + + // Acceleration + ClampVectorForUint(ref acceleration, 64f); + Utils.FloatToUInt16Bytes(acceleration.X, 64.0f, data, pos); pos += 2; + Utils.FloatToUInt16Bytes(acceleration.Y, 64.0f, data, pos); pos += 2; + Utils.FloatToUInt16Bytes(acceleration.Z, 64.0f, data, pos); pos += 2; + + // Rotation + Utils.FloatToUInt16Bytes(rotation.X, 1.0f, data, pos); pos += 2; + Utils.FloatToUInt16Bytes(rotation.Y, 1.0f, data, pos); pos += 2; + Utils.FloatToUInt16Bytes(rotation.Z, 1.0f, data, pos); pos += 2; + Utils.FloatToUInt16Bytes(rotation.W, 1.0f, data, pos); pos += 2; + + // Angular Velocity + ClampVectorForUint(ref angularVelocity, 64f); + Utils.FloatToUInt16Bytes(angularVelocity.X, 64.0f, data, pos); pos += 2; + Utils.FloatToUInt16Bytes(angularVelocity.Y, 64.0f, data, pos); pos += 2; + Utils.FloatToUInt16Bytes(angularVelocity.Z, 64.0f, data, pos); pos += 2; + + // texture entry block size + data[pos++] = 0; + data[pos++] = 0; + // total size 63 or 47 + } + protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(ScenePresence data) { Vector3 offsetPosition = data.OffsetPosition; diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index fca7943c12..d0d21527c4 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs @@ -575,22 +575,22 @@ namespace OpenSim.Region.ClientStack.LindenUDP { DoubleLocklessQueue queue = m_packetOutboxes[category]; - if (m_deliverPackets == false) + if (forceQueue || m_deliverPackets == false) { queue.Enqueue(packet, highPriority); return true; } - TokenBucket bucket = m_throttleCategories[category]; - - // Don't send this packet if queue is not empty + // need to enqueue if queue is not empty if (queue.Count > 0 || m_nextPackets[category] != null) { queue.Enqueue(packet, highPriority); return true; } - if (!forceQueue && bucket.CheckTokens(packet.Buffer.DataLength)) + // check bandwidth + TokenBucket bucket = m_throttleCategories[category]; + if (bucket.CheckTokens(packet.Buffer.DataLength)) { // enough tokens so it can be sent imediatly by caller bucket.RemoveTokens(packet.Buffer.DataLength); @@ -608,7 +608,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // We don't have a token bucket for this category, so it will not be queued return false; } - } /// diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index d324623112..f12b3b9ceb 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -934,6 +934,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Queue or Send } + public void SendUDPPacket( + LLUDPClient udpClient, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method, bool forcequeue) + { + bool highPriority = false; + + if (category != ThrottleOutPacketType.Unknown && (category & ThrottleOutPacketType.HighPriority) != 0) + { + category = (ThrottleOutPacketType)((int)category & 127); + highPriority = true; + } + + OutgoingPacket outgoingPacket = new OutgoingPacket(udpClient, buffer, category, null); + + // If we were not provided a method for handling unacked, use the UDPServer default method + if ((outgoingPacket.Buffer.Data[0] & Helpers.MSG_RELIABLE) != 0) + outgoingPacket.UnackedMethod = ((method == null) ? delegate (OutgoingPacket oPacket) { ResendUnacked(oPacket); } : method); + + if (!outgoingPacket.Client.EnqueueOutgoing(outgoingPacket, forcequeue, highPriority)) + SendPacketFinal(outgoingPacket); + } + public void SendAcks(LLUDPClient udpClient) { uint ack; diff --git a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs index 6f346d3756..49aca3c18e 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/OpenSimUDPBase.cs @@ -489,7 +489,7 @@ namespace OpenMetaverse public void SyncSend(UDPPacketBuffer buf) { if(buf.RemoteEndPoint == null) - return; // was already expired + return; // already expired try { m_udpSocket.SendTo( From 9e182c27fbf598aaa418562a031f6ef8678b2710 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 27 Feb 2019 13:18:38 +0000 Subject: [PATCH 34/62] combat module: those parcel changes are now handled elsewhere --- .../Avatar/Attachments/AttachmentsModule.cs | 9 ++---- .../CoreModules/Avatar/Combat/CombatModule.cs | 28 ------------------- 2 files changed, 2 insertions(+), 35 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs index d36d770152..5205576246 100644 --- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs @@ -953,13 +953,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments return; } - // Saving attachments for NPCs messes them up for the real owner! - INPCModule module = m_scene.RequestModuleInterface(); - if (module != null) - { - if (module.IsNPC(sp.UUID, m_scene)) - return; - } + if(sp.IsNPC) + return; if (grp.HasGroupChanged) { diff --git a/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs b/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs index 4e1958a9c4..10bc6aa188 100644 --- a/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/Combat/CombatModule.cs @@ -77,7 +77,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Combat.CombatModule } scene.EventManager.OnAvatarKilled += KillAvatar; - scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel; } public void RemoveRegion(Scene scene) @@ -86,7 +85,6 @@ namespace OpenSim.Region.CoreModules.Avatar.Combat.CombatModule m_scenel.Remove(scene.RegionInfo.RegionHandle); scene.EventManager.OnAvatarKilled -= KillAvatar; - scene.EventManager.OnAvatarEnteringNewParcel -= AvatarEnteringParcel; } public void RegionLoaded(Scene scene) @@ -177,31 +175,5 @@ namespace OpenSim.Region.CoreModules.Avatar.Combat.CombatModule deadAvatar.setHealthWithUpdate(100.0f); deadAvatar.Scene.TeleportClientHome(deadAvatar.UUID, deadAvatar.ControllingClient); } - - private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, UUID regionID) - { - try - { - ILandObject obj = avatar.Scene.LandChannel.GetLandObject(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y); - if (obj == null) - return; - if ((obj.LandData.Flags & (uint)ParcelFlags.AllowDamage) != 0 - || avatar.Scene.RegionInfo.RegionSettings.AllowDamage) - { - avatar.Invulnerable = false; - } - else - { - avatar.Invulnerable = true; - if (avatar.Health < 100.0f) - { - avatar.setHealthWithUpdate(100.0f); - } - } - } - catch (Exception) - { - } - } } } From bd1b992aaf57154fd57403348a1ab63bfd918ad9 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Wed, 27 Feb 2019 07:49:52 -0800 Subject: [PATCH 35/62] Add Thread.ResetAbort() to various thread loops to clean up errors on shutdown. Fixes Mantis #8494. Threads are aborted when shutting down and ThreadAbortException is odd in that it is rethrown at the end of the catch unless the abort is reset. No functional changes but fewer error messages on shutdown. --- .../HttpServer/PollServiceRequestManager.cs | 34 +++++++++++-------- .../Linden/Caps/GetAssetsModule.cs | 25 +++++++++----- .../Linden/Caps/WebFetchInvDescModule.cs | 23 +++++++++---- .../ClientStack/Linden/UDP/LLUDPServer.cs | 6 +++- .../Api/Implementation/AsyncCommandManager.cs | 9 +++-- 5 files changed, 64 insertions(+), 33 deletions(-) mode change 100644 => 100755 OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs mode change 100644 => 100755 OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs mode change 100644 => 100755 OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs mode change 100644 => 100755 OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs mode change 100644 => 100755 OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs diff --git a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs old mode 100644 new mode 100755 index b9ac155401..14e21a2167 --- a/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs +++ b/OpenSim/Framework/Servers/HttpServer/PollServiceRequestManager.cs @@ -1,4 +1,4 @@ -/* +/* * Copyright (c) Contributors, http://opensimulator.org/ * See CONTRIBUTORS.TXT for a full list of copyright holders. * @@ -230,25 +230,25 @@ namespace OpenSim.Framework.Servers.HttpServer PollServiceHttpRequest req; while (m_running) { - req = null; - if(!m_requests.TryTake(out req, 4500) || req == null) - { - Watchdog.UpdateThread(); - continue; - } - - Watchdog.UpdateThread(); - try { - if(!req.HttpContext.CanSend()) + req = null; + if (!m_requests.TryTake(out req, 4500) || req == null) + { + Watchdog.UpdateThread(); + continue; + } + + Watchdog.UpdateThread(); + + if (!req.HttpContext.CanSend()) { req.PollServiceArgs.Drop(req.RequestID, req.PollServiceArgs.Id); byContextDequeue(req); continue; } - if(req.HttpContext.IsSending()) + if (req.HttpContext.IsSending()) { if ((Environment.TickCount - req.RequestTime) > req.PollServiceArgs.TimeOutms) { @@ -256,7 +256,7 @@ namespace OpenSim.Framework.Servers.HttpServer byContextDequeue(req); } else - ReQueueEvent(req); + ReQueueEvent(req); continue; } @@ -290,7 +290,7 @@ namespace OpenSim.Framework.Servers.HttpServer { nreq.DoHTTPGruntWork(nreq.PollServiceArgs.NoEvents(nreq.RequestID, nreq.PollServiceArgs.Id)); } - catch (ObjectDisposedException) {} + catch (ObjectDisposedException) { } finally { byContextDequeue(nreq); @@ -305,6 +305,12 @@ namespace OpenSim.Framework.Servers.HttpServer } } } + catch (ThreadAbortException) + { + Thread.ResetAbort(); + // Shouldn't set this to 'false', the normal shutdown should cause things to exit + // m_running = false; + } catch (Exception e) { m_log.ErrorFormat("Exception in poll service thread: " + e.ToString()); diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs old mode 100644 new mode 100755 index 9187979903..734425ba16 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs @@ -204,19 +204,26 @@ namespace OpenSim.Region.ClientStack.Linden private static void DoAssetRequests() { - while (m_NumberScenes > 0) + try { - APollRequest poolreq; - if(m_queue.TryTake(out poolreq, 4500)) + while (m_NumberScenes > 0) { - if (m_NumberScenes <= 0) - break; + APollRequest poolreq; + if (m_queue.TryTake(out poolreq, 4500)) + { + if (m_NumberScenes <= 0) + break; + Watchdog.UpdateThread(); + if (poolreq.reqID != UUID.Zero) + poolreq.thepoll.Process(poolreq); + poolreq = null; + } Watchdog.UpdateThread(); - if (poolreq.reqID != UUID.Zero) - poolreq.thepoll.Process(poolreq); - poolreq = null; } - Watchdog.UpdateThread(); + } + catch (ThreadAbortException) + { + Thread.ResetAbort(); } } diff --git a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs old mode 100644 new mode 100755 index 41d70a3ab9..9a01567064 --- a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs @@ -395,17 +395,26 @@ namespace OpenSim.Region.ClientStack.Linden private static void DoInventoryRequests() { - while (true) + bool running = true; + while (running) { - APollRequest poolreq; - if (m_queue.TryTake(out poolreq, 4500)) + try { + APollRequest poolreq; + if (m_queue.TryTake(out poolreq, 4500)) + { + Watchdog.UpdateThread(); + if (poolreq.thepoll != null) + poolreq.thepoll.Process(poolreq); + poolreq = null; + } Watchdog.UpdateThread(); - if (poolreq.thepoll != null) - poolreq.thepoll.Process(poolreq); - poolreq = null; } - Watchdog.UpdateThread(); + catch (ThreadAbortException) + { + Thread.ResetAbort(); + running = false; + } } } } diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs old mode 100644 new mode 100755 index f12b3b9ceb..653f648a3c --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -1910,7 +1910,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP incomingPacket = null; } } - catch(Exception ex) + catch (ThreadAbortException) + { + Thread.ResetAbort(); + } + catch (Exception ex) { m_log.Error("[LLUDPSERVER]: Error in the incoming packet handler loop: " + ex.Message, ex); } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs old mode 100644 new mode 100755 index 3120d04038..a6a2dd890e --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs @@ -221,7 +221,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// private static void CmdHandlerThreadLoop() { - while (true) + bool running = true; + while (running) { try { @@ -230,7 +231,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api DoOneCmdHandlerPass(); Watchdog.UpdateThread(); } - catch ( System.Threading.ThreadAbortException) { } + catch ( System.Threading.ThreadAbortException) + { + Thread.ResetAbort(); + running = false; + } catch (Exception e) { m_log.Error("[ASYNC COMMAND MANAGER]: Exception in command handler pass: ", e); From 87733b196bdc36a21aee775860c962c1141d44af Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 1 Mar 2019 11:48:00 +0000 Subject: [PATCH 36/62] reduce the chance of using a invalid avatar physics actor --- .../Region/Framework/Scenes/ScenePresence.cs | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs index f3e217906c..1c5d23d9b4 100644 --- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs +++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs @@ -507,7 +507,19 @@ namespace OpenSim.Region.Framework.Scenes /// /// Physical scene representation of this Avatar. /// - public PhysicsActor PhysicsActor { get; private set; } + + PhysicsActor m_physActor; + public PhysicsActor PhysicsActor + { + get + { + return m_physActor; + } + private set + { + m_physActor = value; + } + } /// /// Record user movement inputs. @@ -1641,15 +1653,15 @@ namespace OpenSim.Region.Framework.Scenes /// public void RemoveFromPhysicalScene() { - if (PhysicsActor != null) + PhysicsActor pa = Interlocked.Exchange(ref m_physActor, null); + if (pa != null) { // PhysicsActor.OnRequestTerseUpdate -= SendTerseUpdateToAllClients; - PhysicsActor.OnOutOfBounds -= OutOfBoundsCall; - PhysicsActor.OnCollisionUpdate -= PhysicsCollisionUpdate; - PhysicsActor.UnSubscribeEvents(); - m_scene.PhysicsScene.RemoveAvatar(PhysicsActor); - PhysicsActor = null; + pa.OnOutOfBounds -= OutOfBoundsCall; + pa.OnCollisionUpdate -= PhysicsCollisionUpdate; + pa.UnSubscribeEvents(); + m_scene.PhysicsScene.RemoveAvatar(pa); } // else // { @@ -2542,7 +2554,7 @@ namespace OpenSim.Region.Framework.Scenes m_pos.X = 127f; m_pos.Y = 127f; m_pos.Z = 127f; - m_log.Error("[AVATAR]: NonFinite Avatar position detected... Reset Position. Mantis this please. Error #9999903"); + m_log.Error("[AVATAR]: NonFinite Avatar on lastFiniteposition also. Reset Position. Mantis this please. Error #9999903"); } if(isphysical) @@ -5012,16 +5024,17 @@ namespace OpenSim.Region.Framework.Scenes PhysicsScene scene = m_scene.PhysicsScene; Vector3 pVec = AbsolutePosition; - PhysicsActor = scene.AddAvatar( + PhysicsActor pa = scene.AddAvatar( LocalId, Firstname + "." + Lastname, pVec, Appearance.AvatarBoxSize,Appearance.AvatarFeetOffset, isFlying); - PhysicsActor.Orientation = m_bodyRot; + pa.Orientation = m_bodyRot; //PhysicsActor.OnRequestTerseUpdate += SendTerseUpdateToAllClients; - PhysicsActor.OnCollisionUpdate += PhysicsCollisionUpdate; - PhysicsActor.OnOutOfBounds += OutOfBoundsCall; // Called for PhysicsActors when there's something wrong - PhysicsActor.SubscribeEvents(100); - PhysicsActor.LocalID = LocalId; - PhysicsActor.SetAlwaysRun = m_setAlwaysRun; + pa.OnCollisionUpdate += PhysicsCollisionUpdate; + pa.OnOutOfBounds += OutOfBoundsCall; // Called for PhysicsActors when there's something wrong + pa.SubscribeEvents(100); + pa.LocalID = LocalId; + pa.SetAlwaysRun = m_setAlwaysRun; + PhysicsActor = pa; } private void OutOfBoundsCall(Vector3 pos) From 8a8ce8b10a4f595ebe25d52e439b1f1708c4262c Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 1 Mar 2019 11:57:52 +0000 Subject: [PATCH 37/62] put back assetViewer cap code, to allow viewers testing. It is disabled at OpenSimDefaults, and should not be enabled until all major viewers do it right on opensim --- .../ClientStack/Linden/Caps/GetAssetsModule.cs | 14 ++++++-------- bin/OpenSimDefaults.ini | 1 + 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs index 734425ba16..c071bd1847 100755 --- a/OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/GetAssetsModule.cs @@ -59,7 +59,7 @@ namespace OpenSim.Region.ClientStack.Linden private string m_GetTextureURL; private string m_GetMeshURL; private string m_GetMesh2URL; -// private string m_GetAssetURL; + private string m_GetAssetURL; class APollRequest { @@ -87,7 +87,7 @@ namespace OpenSim.Region.ClientStack.Linden private Dictionary m_capsDictTexture = new Dictionary(); private Dictionary m_capsDictGetMesh = new Dictionary(); private Dictionary m_capsDictGetMesh2 = new Dictionary(); - //private Dictionary m_capsDictGetAsset = new Dictionary(); + private Dictionary m_capsDictGetAsset = new Dictionary(); #region Region Module interfaceBase Members @@ -113,11 +113,11 @@ namespace OpenSim.Region.ClientStack.Linden m_GetMesh2URL = config.GetString("Cap_GetMesh2", string.Empty); if (m_GetMesh2URL != string.Empty) m_Enabled = true; -/* + m_GetAssetURL = config.GetString("Cap_GetAsset", string.Empty); if (m_GetAssetURL != string.Empty) m_Enabled = true; -*/ + } public void AddRegion(Scene pScene) @@ -448,7 +448,6 @@ namespace OpenSim.Region.ClientStack.Linden else if (m_GetMesh2URL != string.Empty) caps.RegisterHandler("GetMesh2", m_GetMesh2URL); -/* we can't support this cap. Current viewers connect to the wrong regions. //ViewerAsset if (m_GetAssetURL == "localhost") { @@ -466,7 +465,7 @@ namespace OpenSim.Region.ClientStack.Linden } else if (m_GetAssetURL != string.Empty) caps.RegisterHandler("ViewerAsset", m_GetMesh2URL); -*/ + } private void DeregisterCaps(UUID agentID, Caps caps) @@ -487,13 +486,12 @@ namespace OpenSim.Region.ClientStack.Linden MainServer.Instance.RemovePollServiceHTTPHandler("", capUrl); m_capsDictGetMesh2.Remove(agentID); } -/* + if (m_capsDictGetAsset.TryGetValue(agentID, out capUrl)) { MainServer.Instance.RemovePollServiceHTTPHandler("", capUrl); m_capsDictGetAsset.Remove(agentID); } -*/ } } } diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index bf271ea1ea..3068fee0cb 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -821,6 +821,7 @@ Cap_GetTexture = "localhost" Cap_GetMesh = "localhost" Cap_GetMesh2 = "localhost" + ; Cap_GetAsset = "localhost" DO not ucoment this line. Some popular viewers still dont do it right for opensim. Here to easy testing Cap_GetObjectCost = "" Cap_GetObjectPhysicsData = "" Cap_GroupProposalBallot = "" From 9da1ca2b44556d8c27fb3db104aa2ace2501e4ea Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sat, 2 Mar 2019 15:38:36 +0000 Subject: [PATCH 38/62] ll rez objects: silent ginore null object id; remove redundante llSetColor code --- .../Shared/Api/Implementation/LSL_Api.cs | 41 +------------------ bin/config-include/osslEnable.ini | 4 +- 2 files changed, 3 insertions(+), 42 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index bb4bab0b58..95d7a7a780 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -1936,45 +1936,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (part == null || part.ParentGroup == null || part.ParentGroup.IsDeleted) return; - Primitive.TextureEntry tex = part.Shape.Textures; - int nsides = GetNumberOfSides(part); - Color4 texcolor; - - if (face >= 0 && face < nsides) - { - texcolor = tex.CreateFace((uint)face).RGBA; - texcolor.R = Util.Clip((float)color.x, 0.0f, 1.0f); - texcolor.G = Util.Clip((float)color.y, 0.0f, 1.0f); - texcolor.B = Util.Clip((float)color.z, 0.0f, 1.0f); - tex.FaceTextures[face].RGBA = texcolor; - part.UpdateTextureEntry(tex); - return; - } - else if (face == ScriptBaseClass.ALL_SIDES) - { - for (uint i = 0; i < nsides; i++) - { - if (tex.FaceTextures[i] != null) - { - texcolor = tex.FaceTextures[i].RGBA; - texcolor.R = Util.Clip((float)color.x, 0.0f, 1.0f); - texcolor.G = Util.Clip((float)color.y, 0.0f, 1.0f); - texcolor.B = Util.Clip((float)color.z, 0.0f, 1.0f); - tex.FaceTextures[i].RGBA = texcolor; - } - texcolor = tex.DefaultTexture.RGBA; - texcolor.R = Util.Clip((float)color.x, 0.0f, 1.0f); - texcolor.G = Util.Clip((float)color.y, 0.0f, 1.0f); - texcolor.B = Util.Clip((float)color.z, 0.0f, 1.0f); - tex.DefaultTexture.RGBA = texcolor; - } - part.UpdateTextureEntry(tex); - return; - } - - if (face == ScriptBaseClass.ALL_SIDES) - face = SceneObjectPart.ALL_SIDES; - m_host.SetFaceColorAlpha(face, color, null); } @@ -3577,7 +3538,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public void doObjectRez(string inventory, LSL_Vector pos, LSL_Vector vel, LSL_Rotation rot, int param, bool atRoot) { m_host.AddScriptLPS(1); - if (Double.IsNaN(rot.x) || Double.IsNaN(rot.y) || Double.IsNaN(rot.z) || Double.IsNaN(rot.s)) + if (string.IsNullOrEmpty(inventory) || Double.IsNaN(rot.x) || Double.IsNaN(rot.y) || Double.IsNaN(rot.z) || Double.IsNaN(rot.s)) return; float dist = (float)llVecDist(llGetPos(), pos); diff --git a/bin/config-include/osslEnable.ini b/bin/config-include/osslEnable.ini index a96459ae82..f127b1fe3c 100644 --- a/bin/config-include/osslEnable.ini +++ b/bin/config-include/osslEnable.ini @@ -155,7 +155,6 @@ Allow_osForceDropAttachmentAt = ${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER Allow_osGetLinkPrimitiveParams = ${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER Allow_osGetPhysicsEngineType = true - Allow_osGetPrimitiveParams = ${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER Allow_osGetRegionMapTexture = ${OSSL|osslParcelO}ESTATE_MANAGER,ESTATE_OWNER Allow_osGetScriptEngineName = true Allow_osGetSimulatorVersion = true @@ -185,7 +184,6 @@ Allow_osParcelSubdivide = ESTATE_MANAGER,ESTATE_OWNER Allow_osRegionRestart = ESTATE_MANAGER,ESTATE_OWNER Allow_osRegionNotice = ESTATE_MANAGER,ESTATE_OWNER - Allow_osSetPrimitiveParams = false Allow_osSetProjectionParams = ${OSSL|osslParcelOG}ESTATE_MANAGER,ESTATE_OWNER Allow_osSetRegionWaterHeight = ESTATE_MANAGER,ESTATE_OWNER Allow_osSetStateEvents = false ; deprecated @@ -250,6 +248,7 @@ ; Allow_osGetLinkNumber = true ; Allow_osGetMapTexture = true ; Allow_osGetPhysicsEngineName = true +; Allow_osGetPrimitiveParams = true ; Allow_osGetRegionSize = true ; Allow_osGetSunParam = true ; Allow_osGetTerrainHeight = true @@ -275,6 +274,7 @@ ; Allow_osSetPenCap = true ; Allow_osSetPenColor = true ; Allow_osSetPenSize = true +; Allow_osSetPrimitiveParams = true ; Allow_osSetSoundRadius = true ; Allow_osStopSound = true ; Allow_osStringSubString = true From ca1993c72d53d5052cea4fef56481c9a54645fca Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sat, 2 Mar 2019 18:23:29 +0000 Subject: [PATCH 39/62] direct encode lludp terse object update, let contain texture entry --- OpenSim/Framework/IClientAPI.cs | 1 - .../ClientStack/Linden/UDP/LLClientView.cs | 93 +++++++++++++------ .../Framework/Scenes/SceneObjectPart.cs | 25 +++-- 3 files changed, 78 insertions(+), 41 deletions(-) diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index e660dce2ca..d63136e332 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -671,7 +671,6 @@ namespace OpenSim.Framework Particles = 1 << 19, ExtraData = 1 << 20, Sound = 1 << 21, - Joint = 1 << 22, TerseUpdate = Position | Rotation | Velocity | Acceleration | AngularVelocity, FullUpdate = 0x00ffffff, diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index a9edf081de..da4c1fb032 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -4120,10 +4120,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP float cullingrange = 64.0f; Vector3 mypos = Vector3.Zero; - bool orderedDequeue = m_scene.UpdatePrioritizationScheme == UpdatePrioritizationSchemes.SimpleAngularDistance; + //bool orderedDequeue = m_scene.UpdatePrioritizationScheme == UpdatePrioritizationSchemes.SimpleAngularDistance; + bool orderedDequeue = false; // temporary off HashSet GroupsNeedFullUpdate = new HashSet(); + if (doCulling) { cullingrange = mysp.DrawDistance + m_scene.ReprioritizationDistance + 16f; @@ -4304,7 +4306,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP PrimUpdateFlags.Velocity | PrimUpdateFlags.Acceleration | PrimUpdateFlags.AngularVelocity | - PrimUpdateFlags.CollisionPlane + PrimUpdateFlags.CollisionPlane | + PrimUpdateFlags.Textures ); #endregion UpdateFlags to packet type conversion @@ -4347,7 +4350,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP maxUpdatesBytes -= 18; } terseUpdates.Add(update); - maxUpdatesBytes -= 47; // no texture entry + maxUpdatesBytes -= 47; + if ((updateFlags & PrimUpdateFlags.Textures) != 0) + maxUpdatesBytes -= 100; // aprox } } else @@ -4390,7 +4395,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); //setup header and regioninfo block - Array.Copy(terseUpdateHeader, buf.Data, 7); + Buffer.BlockCopy(terseUpdateHeader, 0, buf.Data, 0, 7); Utils.UInt64ToBytesSafepos(m_scene.RegionInfo.RegionHandle, buf.Data, 7); Utils.UInt16ToBytes(timeDilation, buf.Data, 15); buf.Data[17] = (byte)curNBlocks; @@ -4399,7 +4404,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int count = 0; foreach (EntityUpdate eu in terseAgentUpdates) { - CreateImprovedTerseBlock(eu.Entity, buf.Data, ref pos); + CreateImprovedTerseBlock(eu.Entity, buf.Data, ref pos, false); tau.Add(eu); ++count; --blocks; @@ -4407,7 +4412,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // we need more packets UDPPacketBuffer newbuf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); - Array.Copy(buf.Data, newbuf.Data, 17); // start is the same + Buffer.BlockCopy(buf.Data, 0, newbuf.Data, 0, 17); // start is the same buf.DataLength = pos; m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Unknown, @@ -4455,49 +4460,57 @@ namespace OpenSim.Region.ClientStack.LindenUDP */ if (terseUpdates != null) { - const int maxNBlocks = (LLUDPServer.MTU - 18) / 47; // no texture entry int blocks = terseUpdates.Count; - int curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; - List tau = new List(curNBlocks); + List tau = new List(30); UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); //setup header and regioninfo block - Array.Copy(terseUpdateHeader, buf.Data, 7); + Buffer.BlockCopy(terseUpdateHeader, 0, buf.Data, 0, 7); Utils.UInt64ToBytesSafepos(m_scene.RegionInfo.RegionHandle, buf.Data, 7); Utils.UInt16ToBytes(timeDilation, buf.Data, 15); - buf.Data[17] = (byte)curNBlocks; int pos = 18; + int lastpos = 0; int count = 0; foreach (EntityUpdate eu in terseUpdates) { - CreateImprovedTerseBlock(eu.Entity, buf.Data, ref pos); - tau.Add(eu); - ++count; - --blocks; - if (count == curNBlocks && blocks > 0) + lastpos = pos; + CreateImprovedTerseBlock(eu.Entity, buf.Data, ref pos, (eu.Flags & PrimUpdateFlags.Textures) != 0); + if (pos <= LLUDPServer.MTU) + { + tau.Add(eu); + ++count; + --blocks; + } + else if (blocks > 0) { // we need more packets UDPPacketBuffer newbuf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); - Array.Copy(buf.Data, newbuf.Data, 17); // start is the same + Buffer.BlockCopy(buf.Data, 0, newbuf.Data, 0, 17); // start is the same + // copy what we done in excess + int extralen = pos - lastpos; + if(extralen > 0) + Buffer.BlockCopy(newbuf.Data, 18, buf.Data, lastpos, extralen); - buf.DataLength = pos; + pos = 18 + extralen; + + buf.Data[17] = (byte)count; + buf.DataLength = lastpos; m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false); - curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; - tau = new List(curNBlocks); - count = 0; - + tau = new List(30); + tau.Add(eu); + count = 1; + --blocks; buf = newbuf; - buf.Data[17] = (byte)curNBlocks; - pos = 18; } } if (count > 0) { + buf.Data[17] = (byte)count; buf.DataLength = pos; m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false); @@ -5720,7 +5733,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } - protected void CreateImprovedTerseBlock(ISceneEntity entity, byte[] data, ref int pos) + protected void CreateImprovedTerseBlock(ISceneEntity entity, byte[] data, ref int pos, bool includeTexture) { #region ScenePresence/SOP Handling @@ -5731,6 +5744,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP Vector3 position, velocity, acceleration, angularVelocity; Quaternion rotation; byte datasize; + byte[] te = null; if (avatar) { @@ -5775,6 +5789,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP rotation = part.RotationOffset; datasize = 44; + if(includeTexture) + te = part.Shape.TextureEntry; } #endregion ScenePresence/SOP Handling @@ -5785,8 +5801,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP Utils.UIntToBytes(localID, data, pos); pos += 4; - // Avatar/CollisionPlane data[pos++] = (byte)attachPoint; + + // Avatar/CollisionPlane if (avatar) { data[pos++] = 1; @@ -5829,11 +5846,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP Utils.FloatToUInt16Bytes(angularVelocity.X, 64.0f, data, pos); pos += 2; Utils.FloatToUInt16Bytes(angularVelocity.Y, 64.0f, data, pos); pos += 2; Utils.FloatToUInt16Bytes(angularVelocity.Z, 64.0f, data, pos); pos += 2; - + // texture entry block size - data[pos++] = 0; - data[pos++] = 0; - // total size 63 or 47 + if(te == null) + { + data[pos++] = 0; + data[pos++] = 0; + } + else + { + int len = te.Length & 0x7fff; + int totlen = len + 4; + data[pos++] = (byte)totlen; + data[pos++] = (byte)(totlen >> 8); + data[pos++] = (byte)len; // wtf ??? + data[pos++] = (byte)(len >> 8); + data[pos++] = 0; + data[pos++] = 0; + Buffer.BlockCopy(te, 0, data, pos, len); + pos += len; + } + // total size 63 or 47 + (texture size + 4) } protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(ScenePresence data) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 23bef74704..a1296ba96b 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -3242,8 +3242,6 @@ namespace OpenSim.Region.Framework.Scenes if (ParentGroup.Scene.GetNumberOfClients() == 0) return; - ParentGroup.QueueForUpdateCheck(); - bool isfull = false; if (ParentGroup.IsAttachment) { @@ -3254,6 +3252,8 @@ namespace OpenSim.Region.Framework.Scenes lock (UpdateFlagLock) UpdateFlag |= update; + ParentGroup.QueueForUpdateCheck(); + ParentGroup.Scene.EventManager.TriggerSceneObjectPartUpdated(this, isfull); } @@ -5133,8 +5133,7 @@ namespace OpenSim.Region.Framework.Scenes m_shape.TextureEntry = newTex.GetBytes(); TriggerScriptChangedEvent(changeFlags); ParentGroup.HasGroupChanged = true; - ScheduleFullUpdate(); - + ScheduleUpdate(PrimUpdateFlags.Textures); } /// @@ -5163,7 +5162,7 @@ namespace OpenSim.Region.Framework.Scenes m_shape.TextureEntry = newTex.GetBytes(); TriggerScriptChangedEvent(changeFlags); ParentGroup.HasGroupChanged = true; - ScheduleFullUpdate(); + ScheduleUpdate(PrimUpdateFlags.Textures); } internal void UpdatePhysicsSubscribedEvents() @@ -5575,20 +5574,26 @@ namespace OpenSim.Region.Framework.Scenes // handle osVolumeDetect public void ScriptSetVolumeDetect(bool makeVolumeDetect) { + if(ParentGroup.IsDeleted) + return; + if(_parentID == 0) { - // if root prim do it via SOG + // if root prim do it is like llVolumeDetect ParentGroup.ScriptSetVolumeDetect(makeVolumeDetect); return; } - bool wasUsingPhysics = ((Flags & PrimFlags.Physics) != 0); - bool wasTemporary = ((Flags & PrimFlags.TemporaryOnRez) != 0); - bool wasPhantom = ((Flags & PrimFlags.Phantom) != 0); + if(ParentGroup.IsVolumeDetect) + return; // entire linkset is phantom already + + bool wasUsingPhysics = ParentGroup.UsesPhysics; + bool wasTemporary = ParentGroup.IsTemporary; + bool wasPhantom = ParentGroup.IsPhantom; if(PhysActor != null) PhysActor.Building = true; - UpdatePrimFlags(wasUsingPhysics,wasTemporary,wasPhantom,makeVolumeDetect,false); + UpdatePrimFlags(wasUsingPhysics, wasTemporary, wasPhantom, makeVolumeDetect, false); } protected static int m_animationSequenceNumber = (int)(Util.GetTimeStampTicks() & 0x5fffafL); From 80056abbe71a5946b6f20d33069e12c952af9eeb Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sun, 3 Mar 2019 12:15:28 +0000 Subject: [PATCH 40/62] OOOPPPSSS --- OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index da4c1fb032..e039fbf3bd 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -4477,7 +4477,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { lastpos = pos; CreateImprovedTerseBlock(eu.Entity, buf.Data, ref pos, (eu.Flags & PrimUpdateFlags.Textures) != 0); - if (pos <= LLUDPServer.MTU) + if (pos < LLUDPServer.MTU) { tau.Add(eu); ++count; @@ -4491,7 +4491,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // copy what we done in excess int extralen = pos - lastpos; if(extralen > 0) - Buffer.BlockCopy(newbuf.Data, 18, buf.Data, lastpos, extralen); + Buffer.BlockCopy(buf.Data, lastpos, newbuf.Data, 18, extralen); pos = 18 + extralen; From b5ad1b7dcc3592dfa49298636948ea117132e6ff Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Tue, 5 Mar 2019 09:22:34 +0000 Subject: [PATCH 41/62] remove lludp throttle texture rate cannibal option. That rate is used by http, and beeing http is still trafic --- .../Region/ClientStack/Linden/UDP/LLUDPClient.cs | 14 -------------- .../Region/ClientStack/Linden/UDP/ThrottleRates.cs | 9 --------- 2 files changed, 23 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs index d0d21527c4..2981337b2d 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPClient.cs @@ -210,12 +210,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - /// - /// This is the percentage of the udp texture queue to add to the task queue since - /// textures are now generally handled through http. - /// - private double m_cannibalrate = 0.0; - private ClientInfo m_info = new ClientInfo(); /// @@ -257,8 +251,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // Create an array of token buckets for this clients different throttle categories m_throttleCategories = new TokenBucket[THROTTLE_CATEGORY_COUNT]; - m_cannibalrate = rates.CannibalizeTextureRate; - m_burst = rates.Total * rates.BrustTime; for (int i = 0; i < THROTTLE_CATEGORY_COUNT; i++) @@ -449,12 +441,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP asset = Math.Max(asset, LLUDPServer.MTU); */ - // Since most textures are now delivered through http, make it possible - // to cannibalize some of the bw from the texture throttle to use for - // the task queue (e.g. object updates) - task = task + (int)(m_cannibalrate * texture); - texture = (int)((1 - m_cannibalrate) * texture); - int total = resend + land + wind + cloud + task + texture + asset; float m_burst = total * m_burstTime; diff --git a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs index f8ec97ae83..3277638697 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/ThrottleRates.cs @@ -66,9 +66,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public Int64 MinimumAdaptiveThrottleRate; - /// Amount of the texture throttle to steal for the task throttle - public double CannibalizeTextureRate; - public int ClientMaxRate; public float BrustTime; @@ -104,12 +101,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP // AdaptiveThrottlesEnabled = throttleConfig.GetBoolean("enable_adaptive_throttles", false); AdaptiveThrottlesEnabled = false; MinimumAdaptiveThrottleRate = throttleConfig.GetInt("adaptive_throttle_min_bps", 32000); - - // http textures do use udp bandwidth setting -// CannibalizeTextureRate = (double)throttleConfig.GetFloat("CannibalizeTextureRate", 0.0f); -// CannibalizeTextureRate = Util.Clamp(CannibalizeTextureRate,0.0, 0.9); - CannibalizeTextureRate = 0f; - } catch (Exception) { } } From 0944a965172668827a3f1cd83c1f99230299cbb7 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Tue, 5 Mar 2019 16:01:29 +0000 Subject: [PATCH 42/62] llupd direct encode object updates for agents; let terse updates be zeroencoded. This is not as spec but does work --- .../ClientStack/Linden/UDP/LLClientView.cs | 251 +++++++++++------- .../ClientStack/Linden/UDP/LLUDPServer.cs | 70 ++++- bin/OpenSimDefaults.ini | 21 -- 3 files changed, 212 insertions(+), 130 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index e039fbf3bd..f8ff3c4ec6 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -3891,29 +3891,40 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (ent == null) return; - ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); - objupdate.Header.Zerocoded = true; - - objupdate.RegionData.TimeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); - objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; - - if(ent is ScenePresence) + if (ent is ScenePresence) { ScenePresence presence = ent as ScenePresence; - objupdate.RegionData.RegionHandle = presence.RegionHandle; - objupdate.ObjectData[0] = CreateAvatarUpdateBlock(presence); + + UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + + //setup header and regioninfo block + Buffer.BlockCopy(objectUpdateHeader, 0, buf.Data, 0, 7); + Utils.UInt64ToBytesSafepos(m_scene.RegionInfo.RegionHandle, buf.Data, 7); + Utils.UInt16ToBytes(Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f), buf.Data, 15); + + buf.Data[17] = 1; + int pos = 18; + CreateAvatarUpdateBlock(presence, buf.Data, ref pos); + + buf.DataLength = pos; + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority, null, false, true); } + else if(ent is SceneObjectPart) { + ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); + objupdate.RegionData.TimeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); + objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; + SceneObjectPart part = ent as SceneObjectPart; objupdate.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; - objupdate.ObjectData[0] = CreatePrimUpdateBlock(part, (ScenePresence)SceneAgent); + objupdate.ObjectData[0] = CreatePrimUpdateBlock(part, (ScenePresence)SceneAgent); + + OutPacket(objupdate, ThrottleOutPacketType.Task); } - OutPacket(objupdate, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); - // We need to record the avatar local id since the root prim of an attachment points to this. -// m_attachmentsSent.Add(avatar.LocalId); + // m_attachmentsSent.Add(avatar.LocalId); } public void SendEntityTerseUpdateImmediate(ISceneEntity ent) @@ -4084,8 +4095,15 @@ namespace OpenSim.Region.ClientStack.LindenUDP ResendPrimUpdate(update); } + static private readonly byte[] objectUpdateHeader = new byte[] { + Helpers.MSG_RELIABLE | Helpers.MSG_ZEROCODED, + 0, 0, 0, 0, // sequence number + 0, // extra + 12 // ID (high frequency) + }; + static private readonly byte[] terseUpdateHeader = new byte[] { - Helpers.MSG_RELIABLE, + Helpers.MSG_RELIABLE | Helpers.MSG_ZEROCODED, // zero code is not as spec 0, 0, 0, 0, // sequence number 0, // extra 15 // ID (high frequency) @@ -4105,7 +4123,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP List objectUpdates = null; // List compressedUpdates = null; List terseUpdates = null; - List terseAgentUpdates = null; List ObjectAnimationUpdates = null; // Check to see if this is a flush @@ -4275,7 +4292,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if(ObjectAnimationUpdates == null) ObjectAnimationUpdates = new List(); ObjectAnimationUpdates.Add(sop); - maxUpdatesBytes -= 32 * sop.Animations.Count + 16; + maxUpdatesBytes -= 20 * sop.Animations.Count + 24; } } } @@ -4329,37 +4346,30 @@ namespace OpenSim.Region.ClientStack.LindenUDP if ((updateFlags & canNotUseImprovedMask) == 0) { + if (terseUpdates == null) + { + terseUpdates = new List(); + maxUpdatesBytes -= 18; + } + terseUpdates.Add(update); if (update.Entity is ScenePresence) - { - // ALL presence updates go into a special list - if (terseAgentUpdates == null) - { - terseAgentUpdates = new List(); - maxUpdatesBytes -= 18; - } - terseAgentUpdates.Add(update); maxUpdatesBytes -= 63; // no texture entry - } else { - // Everything else goes here - if (terseUpdates == null) - { - terseUpdates = new List(); - maxUpdatesBytes -= 18; - } - terseUpdates.Add(update); - maxUpdatesBytes -= 47; - if ((updateFlags & PrimUpdateFlags.Textures) != 0) - maxUpdatesBytes -= 100; // aprox + if ((updateFlags & PrimUpdateFlags.Textures) == 0) + maxUpdatesBytes -= 47; + else + maxUpdatesBytes -= 150; // aprox } } else { ObjectUpdatePacket.ObjectDataBlock ablock; if (update.Entity is ScenePresence) + { ablock = CreateAvatarUpdateBlock((ScenePresence)update.Entity); + } else ablock = CreatePrimUpdateBlock((SceneObjectPart)update.Entity, mysp); if(objectUpdateBlocks == null) @@ -4385,57 +4395,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP timeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); - if (terseAgentUpdates != null) - { - const int maxNBlocks = (LLUDPServer.MTU - 18) / 63; // no texture entry - int blocks = terseAgentUpdates.Count; - int curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; - List tau = new List(curNBlocks); - - UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); - - //setup header and regioninfo block - Buffer.BlockCopy(terseUpdateHeader, 0, buf.Data, 0, 7); - Utils.UInt64ToBytesSafepos(m_scene.RegionInfo.RegionHandle, buf.Data, 7); - Utils.UInt16ToBytes(timeDilation, buf.Data, 15); - buf.Data[17] = (byte)curNBlocks; - int pos = 18; - - int count = 0; - foreach (EntityUpdate eu in terseAgentUpdates) - { - CreateImprovedTerseBlock(eu.Entity, buf.Data, ref pos, false); - tau.Add(eu); - ++count; - --blocks; - if (count == curNBlocks && blocks > 0) - { - // we need more packets - UDPPacketBuffer newbuf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); - Buffer.BlockCopy(buf.Data, 0, newbuf.Data, 0, 17); // start is the same - - buf.DataLength = pos; - m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Unknown, - delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false); - - curNBlocks = blocks > maxNBlocks ? maxNBlocks : blocks; - tau = new List(curNBlocks); - count = 0; - - buf = newbuf; - buf.Data[17] = (byte)curNBlocks; - pos = 18; - } - } - - if (count > 0) - { - buf.DataLength = pos; - m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Unknown, - delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false); - } - } - if (objectUpdateBlocks != null) { ObjectUpdatePacket packet = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); @@ -4497,8 +4456,9 @@ namespace OpenSim.Region.ClientStack.LindenUDP buf.Data[17] = (byte)count; buf.DataLength = lastpos; + // zero encode is not as spec m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, - delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false); + delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, true); tau = new List(30); tau.Add(eu); @@ -4513,7 +4473,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP buf.Data[17] = (byte)count; buf.DataLength = pos; m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, - delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false); + delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, true); } } @@ -4534,13 +4494,11 @@ namespace OpenSim.Region.ClientStack.LindenUDP UUID[] ids = null; int[] seqs = null; int count = sop.GetAnimations(out ids, out seqs); - if(count < 0) - continue; ObjectAnimationPacket ani = (ObjectAnimationPacket)PacketPool.Instance.GetPacket(PacketType.ObjectAnimation); ani.Sender = new ObjectAnimationPacket.SenderBlock(); ani.Sender.ID = sop.UUID; - ani.AnimationList = new ObjectAnimationPacket.AnimationListBlock[sop.Animations.Count]; + ani.AnimationList = new ObjectAnimationPacket.AnimationListBlock[count]; for(int i = 0; i< count; i++) { @@ -5732,7 +5690,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP return block; } - protected void CreateImprovedTerseBlock(ISceneEntity entity, byte[] data, ref int pos, bool includeTexture) { #region ScenePresence/SOP Handling @@ -5871,7 +5828,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP protected ObjectUpdatePacket.ObjectDataBlock CreateAvatarUpdateBlock(ScenePresence data) { - Vector3 offsetPosition = data.OffsetPosition; Quaternion rotation = data.Rotation; // tpvs can only see rotations around Z in some cases if(!data.Flying && !data.IsSatOnObject) @@ -5881,27 +5837,26 @@ namespace OpenSim.Region.ClientStack.LindenUDP } rotation.Normalize(); - uint parentID = data.ParentID; - // m_log.DebugFormat( // "[LLCLIENTVIEW]: Sending full update to {0} with pos {1}, vel {2} in {3}", Name, data.OffsetPosition, data.Velocity, m_scene.Name); byte[] objectData = new byte[76]; - Vector3 velocity = new Vector3(0, 0, 0); - Vector3 acceleration = new Vector3(0, 0, 0); + //Vector3 velocity = Vector3.Zero; + Vector3 acceleration = Vector3.Zero; + Vector3 angularvelocity = Vector3.Zero; data.CollisionPlane.ToBytes(objectData, 0); - offsetPosition.ToBytes(objectData, 16); - velocity.ToBytes(objectData, 28); + data.OffsetPosition.ToBytes(objectData, 16); + data.Velocity.ToBytes(objectData, 28); acceleration.ToBytes(objectData, 40); rotation.ToBytes(objectData, 52); - data.AngularVelocity.ToBytes(objectData, 64); + angularvelocity.ToBytes(objectData, 64); ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); update.Data = Utils.EmptyBytes; - update.ExtraParams = new byte[1]; + update.ExtraParams = Utils.EmptyBytes; update.FullID = data.UUID; update.ID = data.LocalId; update.Material = (byte)Material.Flesh; @@ -5938,7 +5893,99 @@ namespace OpenSim.Region.ClientStack.LindenUDP return update; } -// protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart data, UUID recipientID) + protected void CreateAvatarUpdateBlock(ScenePresence data, byte[] dest, ref int pos) + { + Quaternion rotation = data.Rotation; + // tpvs can only see rotations around Z in some cases + if (!data.Flying && !data.IsSatOnObject) + { + rotation.X = 0f; + rotation.Y = 0f; + } + rotation.Normalize(); + + //Vector3 velocity = Vector3.Zero; + //Vector3 acceleration = Vector3.Zero; + //Vector3 angularvelocity = Vector3.Zero; + + Utils.UIntToBytesSafepos(data.LocalId, dest, pos); pos += 4; + dest[pos++] = 0; // state + data.UUID.ToBytes(dest, pos); pos += 16; + Utils.UIntToBytesSafepos(0 , dest, pos); pos += 4; // crc + dest[pos++] = (byte)PCode.Avatar; + dest[pos++] = (byte)Material.Flesh; + dest[pos++] = 0; // clickaction + data.Appearance.AvatarSize.ToBytes(dest, pos); pos += 12; + + // objectdata block + dest[pos++] = 76; + data.CollisionPlane.ToBytes(dest, pos); pos += 16; + data.OffsetPosition.ToBytes(dest, pos); pos += 12; + data.Velocity.ToBytes(dest, pos); pos += 12; + + //acceleration.ToBytes(dest, pos); pos += 12; + Array.Clear(dest, pos, 12); pos += 12; + + rotation.ToBytes(dest, pos); pos += 12; + + //angularvelocity.ToBytes(dest, pos); pos += 12; + Array.Clear(dest, pos, 12); pos += 12; + + SceneObjectPart parentPart = data.ParentPart; + if (parentPart != null) + { + Utils.UIntToBytesSafepos(parentPart.ParentGroup.LocalId, dest, pos); + pos += 4; + } + else + { +// Utils.UIntToBytesSafepos(0, dest, pos); +// pos += 4; + dest[pos++] = 0; + dest[pos++] = 0; + dest[pos++] = 0; + dest[pos++] = 0; + } + + //Utils.UIntToBytesSafepos(0, dest, pos); pos += 4; //update flags + dest[pos++] = 0; + dest[pos++] = 0; + dest[pos++] = 0; + dest[pos++] = 0; + + //pbs + dest[pos++] = 16; + dest[pos++] = 1; + //Utils.UInt16ToBytes(0, dest, pos); pos += 2; + //Utils.UInt16ToBytes(0, dest, pos); pos += 2; + dest[pos++] = 0; + dest[pos++] = 0; + dest[pos++] = 0; + dest[pos++] = 0; + + dest[pos++] = 100; + dest[pos++] = 100; + + // rest of pbs is 0 (15), texture entry (2) and texture anim (1) + const int pbszeros = 15 + 2 + 1; + Array.Clear(dest, pos, pbszeros); pos += pbszeros; + + //NameValue + byte[] nv = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " + + data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle); + int len = nv.Length; + dest[pos++] = (byte)len; + dest[pos++] = (byte)(len >> 8); + Buffer.BlockCopy(nv, 0, dest, pos, len); pos += len; + + // data(2), text(1), text color(4), media url(1), PBblock(1), ExtramParams(1), + // sound id(16), sound owner(16) gain (4), flags (1), radius (4) + // jointtype(1) joint pivot(12) joint offset(12) + const int lastzeros = 2 + 1 + 4 + 1 + 1 + 1 + 16 + 16 + 4 + 1 + 4 + 1 + 12 + 12; + Array.Clear(dest, pos, lastzeros); pos += lastzeros; + } + + // protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart data, UUID recipientID) protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart part, ScenePresence sp) { byte[] objectData = new byte[60]; diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 653f648a3c..6fd782a174 100755 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -274,10 +274,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// The measured resolution of Environment.TickCount public readonly float TickCountResolution; - /// Number of prim updates to put on the queue each time the - /// OnQueueEmpty event is triggered for updates - public readonly int PrimUpdatesPerCallback; - /// Number of texture packets to put on the queue each time the /// OnQueueEmpty event is triggered for textures public readonly int TextureSendLimit; @@ -440,7 +436,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP m_recvBufferSize = config.GetInt("client_socket_rcvbuf_size", 0); sceneThrottleBps = config.GetInt("scene_throttle_max_bps", 0); - PrimUpdatesPerCallback = config.GetInt("PrimUpdatesPerCallback", 100); TextureSendLimit = config.GetInt("TextureSendLimit", 20); m_defaultRTO = config.GetInt("DefaultRTO", 0); @@ -451,7 +446,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - PrimUpdatesPerCallback = 100; TextureSendLimit = 20; m_ackTimeout = 1000 * 60; // 1 minute m_pausedAckTimeout = 1000 * 300; // 5 minutes @@ -934,11 +928,73 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion Queue or Send } + public unsafe UDPPacketBuffer ZeroEncode(UDPPacketBuffer input) + { + UDPPacketBuffer zb = GetNewUDPBuffer(null); + int srclen = input.DataLength; + byte[] src = input.Data; + byte[] dest = zb.Data; + + int zerolen = 6; + byte zerocount = 0; + + for (int i = zerolen; i < srclen; i++) + { + if (src[i] == 0x00) + { + zerocount++; + if (zerocount == 0) + { + dest[zerolen++] = 0x00; + dest[zerolen++] = 0xff; + zerocount++; + } + } + else + { + if (zerocount != 0) + { + dest[zerolen++] = 0x00; + dest[zerolen++] = zerocount; + zerocount = 0; + } + + dest[zerolen++] = src[i]; + } + } + + if (zerocount != 0) + { + dest[zerolen++] = 0x00; + dest[zerolen++] = zerocount; + } + + if(zerolen >= srclen) + { + FreeUDPBuffer(zb); + + src[0] &= unchecked((byte)~Helpers.MSG_ZEROCODED); + return input; + } + + Buffer.BlockCopy(src, 0, dest, 0, 6); + + zb.RemoteEndPoint = input.RemoteEndPoint; + zb.DataLength = zerolen; + + FreeUDPBuffer(input); + + return zb; + } + public void SendUDPPacket( - LLUDPClient udpClient, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method, bool forcequeue) + LLUDPClient udpClient, UDPPacketBuffer buffer, ThrottleOutPacketType category, UnackedPacketMethod method, bool forcequeue, bool zerocode) { bool highPriority = false; + if(zerocode) + buffer = ZeroEncode(buffer); + if (category != ThrottleOutPacketType.Unknown && (category & ThrottleOutPacketType.HighPriority) != 0) { category = (ThrottleOutPacketType)((int)category & 127); diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 3068fee0cb..cede84d25b 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -747,17 +747,6 @@ ;texture_default = 18500 ;asset_default = 10500 - ; Configures how ObjectUpdates are aggregated. These numbers - ; do not literally mean how many updates will be put in each - ; packet that goes over the wire, as packets are - ; automatically split on a 1400 byte boundary. These control - ; the balance between responsiveness of interest list updates - ; and total throughput. Higher numbers will ensure more full- - ; sized packets and faster sending of data, but more delay in - ; updating interest lists - ; - ;PrimUpdatesPerCallback = 100 - ; TextureSendLimit determines how many packets will be put on ; the outgoing queue each cycle. Like the settings above, this ; is a balance between responsiveness to priority updates and @@ -767,16 +756,6 @@ ; ;TextureSendLimit = 20 - ; CannibalizeTextureRate allows bandwidth to be moved from the - ; UDP texture throttle to the task throttle. Since most viewers - ; use HTTP textures, this provides a means of using what is largely - ; unused bandwidth in the total throttle. The value is the proportion - ; of the texture rate to move to the task queue. It must be between - ; 0.0 (none of the bandwidth is cannibalized) and 0.9 (90% of the - ; bandwidth is grabbed) - ; - ; CannibalizeTextureRate = 0.5 - ; Quash and remove any light properties from attachments not on the ; hands. This allows flashlights and lanterns to function, but kills ; silly vanity "Facelights" dead. Sorry, head mounted miner's lamps From 39f73b82d4d0a5a43443c8275c8fe94297c51410 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Tue, 5 Mar 2019 17:03:17 +0000 Subject: [PATCH 43/62] dont try to backup a object in the middle of possible multipack link --- OpenSim/Region/Framework/Scenes/SceneGraph.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs index b526fe936b..ea037beb30 100755 --- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs +++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs @@ -1913,9 +1913,8 @@ namespace OpenSim.Region.Framework.Scenes { if (parentGroup.OwnerID == child.OwnerID) { - parentGroup.LinkToGroup(child); - child.DetachFromBackup(); + parentGroup.LinkToGroup(child); // this is here so physics gets updated! // Don't remove! Bad juju! Stay away! or fix physics! @@ -1943,7 +1942,6 @@ namespace OpenSim.Region.Framework.Scenes */ parentGroup.AdjustChildPrimPermissions(false); parentGroup.HasGroupChanged = true; - parentGroup.ProcessBackup(m_parentScene.SimulationDataService, true); parentGroup.ScheduleGroupForFullAnimUpdate(); Monitor.Exit(m_linkLock); } From 87c81b5172e98a31405d924cbdb4b7ef271f3c38 Mon Sep 17 00:00:00 2001 From: Robert Adams Date: Wed, 6 Mar 2019 10:29:46 -0800 Subject: [PATCH 44/62] BulletSim: Add delay to stationary check after adding force to Avatar. Fix to Mantis 8496. Add parameter [BulletSim] AvatarAddForceFrames. --- .../BulletS/BSActorAvatarMove.cs | 24 ++++++++++++------- .../PhysicsModules/BulletS/BSCharacter.cs | 2 +- .../Region/PhysicsModules/BulletS/BSParam.cs | 3 +++ 3 files changed, 19 insertions(+), 10 deletions(-) mode change 100644 => 100755 OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs b/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs index 40c6b983b0..4e9216db30 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSActorAvatarMove.cs @@ -47,9 +47,9 @@ public class BSActorAvatarMove : BSActor // The amount the step up is applying. Used to smooth stair walking. float m_lastStepUp; - // There are times the velocity is set but we don't want to inforce stationary until the - // real velocity drops. - bool m_waitingForLowVelocityForStationary = false; + // There are times the velocity or force is set but we don't want to inforce + // stationary until some tick in the future and the real velocity drops. + int m_waitingForLowVelocityForStationary = 0; public BSActorAvatarMove(BSScene physicsScene, BSPhysObject pObj, string actorName) : base(physicsScene, pObj, actorName) @@ -114,14 +114,18 @@ public class BSActorAvatarMove : BSActor m_velocityMotor.Enabled = true; m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,SetVelocityAndTarget,vel={1}, targ={2}", m_controllingPrim.LocalID, vel, targ); - m_waitingForLowVelocityForStationary = false; + m_waitingForLowVelocityForStationary = 0; } }); } public void SuppressStationayCheckUntilLowVelocity() { - m_waitingForLowVelocityForStationary = true; + m_waitingForLowVelocityForStationary = 1; + } + public void SuppressStationayCheckUntilLowVelocity(int waitTicks) + { + m_waitingForLowVelocityForStationary = waitTicks; } // If a movement motor has not been created, create one and start the movement @@ -143,7 +147,7 @@ public class BSActorAvatarMove : BSActor m_controllingPrim.OnPreUpdateProperty += Process_OnPreUpdateProperty; m_walkingUpStairs = 0; - m_waitingForLowVelocityForStationary = false; + m_waitingForLowVelocityForStationary = 0; } } @@ -194,15 +198,17 @@ public class BSActorAvatarMove : BSActor // if colliding with something stationary and we're not doing volume detect . if (!m_controllingPrim.ColliderIsMoving && !m_controllingPrim.ColliderIsVolumeDetect) { - if (m_waitingForLowVelocityForStationary) + if (m_waitingForLowVelocityForStationary-- <= 0) { // if waiting for velocity to drop and it has finally dropped, we can be stationary + // m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,waitingForLowVelocity {1}", + // m_controllingPrim.LocalID, m_waitingForLowVelocityForStationary); if (m_controllingPrim.RawVelocity.LengthSquared() < BSParam.AvatarStopZeroThresholdSquared) { - m_waitingForLowVelocityForStationary = false; + m_waitingForLowVelocityForStationary = 0; } } - if (!m_waitingForLowVelocityForStationary) + if (m_waitingForLowVelocityForStationary <= 0) { m_physicsScene.DetailLog("{0},BSCharacter.MoveMotor,collidingWithStationary,zeroingMotion", m_controllingPrim.LocalID); m_controllingPrim.IsStationary = true; diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs old mode 100644 new mode 100755 index 2ca7dbc498..f971e59166 --- a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs @@ -701,7 +701,7 @@ public sealed class BSCharacter : BSPhysObject } if (m_moveActor != null) { - m_moveActor.SuppressStationayCheckUntilLowVelocity(); + m_moveActor.SuppressStationayCheckUntilLowVelocity(BSParam.AvatarAddForceFrames); } }); } diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs b/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs index 495f752e46..d80b050de6 100755 --- a/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs +++ b/OpenSim/Region/PhysicsModules/BulletS/BSParam.cs @@ -149,6 +149,7 @@ public static class BSParam public static float AvatarHeightHighFudge { get; private set; } public static float AvatarFlyingGroundMargin { get; private set; } public static float AvatarFlyingGroundUpForce { get; private set; } + public static int AvatarAddForceFrames { get; private set; } public static float AvatarTerminalVelocity { get; private set; } public static float AvatarContactProcessingThreshold { get; private set; } public static float AvatarAddForcePushFactor { get; private set; } @@ -634,6 +635,8 @@ public static class BSParam 5f ), new ParameterDefn("AvatarFlyingGroundUpForce", "Upward force applied to the avatar to keep it at flying ground margin", 2.0f ), + new ParameterDefn("AvatarAddForceFrames", "Frames to allow AddForce to apply before checking for stationary", + 10 ), new ParameterDefn("AvatarTerminalVelocity", "Terminal Velocity of falling avatar", -54.0f ), new ParameterDefn("AvatarContactProcessingThreshold", "Distance from capsule to check for collisions", From cf0f3954a80c480e34c2e5556e2b0581c4c23f88 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 6 Mar 2019 20:00:39 +0000 Subject: [PATCH 45/62] lludp do inline zeroencode of some(most) agent full updates (runprebuild) --- .../ClientStack/Linden/UDP/LLClientView.cs | 114 ++++--- .../Linden/UDP/LLUDPZeroEncoder.cs | 279 ++++++++++++++++++ 2 files changed, 356 insertions(+), 37 deletions(-) create mode 100644 OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index f8ff3c4ec6..dd06e402c8 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -3894,22 +3894,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (ent is ScenePresence) { ScenePresence presence = ent as ScenePresence; - UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); - //setup header and regioninfo block Buffer.BlockCopy(objectUpdateHeader, 0, buf.Data, 0, 7); - Utils.UInt64ToBytesSafepos(m_scene.RegionInfo.RegionHandle, buf.Data, 7); - Utils.UInt16ToBytes(Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f), buf.Data, 15); - buf.Data[17] = 1; - int pos = 18; - CreateAvatarUpdateBlock(presence, buf.Data, ref pos); - - buf.DataLength = pos; - m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority, null, false, true); + LLUDPZeroEncoder zc = new LLUDPZeroEncoder(buf.Data); + zc.Position = 7; + zc.AddUInt64(m_scene.RegionInfo.RegionHandle); + zc.AddUInt16(Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f)); + zc.AddByte(1); // block count + CreateAvatarUpdateBlock(presence, zc); + buf.DataLength = zc.Finish(); + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority, null, false, false); } - else if(ent is SceneObjectPart) { ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); @@ -5985,6 +5982,75 @@ namespace OpenSim.Region.ClientStack.LindenUDP Array.Clear(dest, pos, lastzeros); pos += lastzeros; } + protected void CreateAvatarUpdateBlock(ScenePresence data, LLUDPZeroEncoder zc) + { + Quaternion rotation = data.Rotation; + // tpvs can only see rotations around Z in some cases + if (!data.Flying && !data.IsSatOnObject) + { + rotation.X = 0f; + rotation.Y = 0f; + } + rotation.Normalize(); + + zc.AddUInt(data.LocalId); + zc.AddByte(0); + zc.AddUUID(data.UUID); + zc.AddZeros(4); // crc unused + zc.AddByte((byte)PCode.Avatar); + zc.AddByte((byte)Material.Flesh); + zc.AddByte(0); // clickaction + zc.AddVector3(data.Appearance.AvatarSize); + + // objectdata block + zc.AddByte(76); // fixed avatar block size + zc.AddVector4(data.CollisionPlane); + zc.AddVector3(data.OffsetPosition); + zc.AddVector3(data.Velocity); + //zc.AddVector3(acceleration); + zc.AddZeros(12); + zc.AddNormQuat(rotation); + //zc.AddVector3(angularvelocity); + zc.AddZeros(12); + + SceneObjectPart parentPart = data.ParentPart; + if (parentPart != null) + zc.AddUInt(parentPart.ParentGroup.LocalId); + else + zc.AddZeros(4); + + zc.AddZeros(4); //update flags + + //pbs + zc.AddByte(16); + zc.AddByte(1); + //Utils.UInt16ToBytes(0, dest, pos); pos += 2; + //Utils.UInt16ToBytes(0, dest, pos); pos += 2; + zc.AddZeros(4); + + zc.AddByte(100); + zc.AddByte(100); + + // rest of pbs is 0 (15), texture entry (2) and texture anim (1) + const int pbszeros = 15 + 2 + 1; + zc.AddZeros(pbszeros); + + //NameValue + byte[] nv = Utils.StringToBytes("FirstName STRING RW SV " + data.Firstname + "\nLastName STRING RW SV " + + data.Lastname + "\nTitle STRING RW SV " + data.Grouptitle); + int len = nv.Length; + zc.AddByte((byte)len); + zc.AddByte((byte)(len >> 8)); + zc.AddBytes(nv, len); + + // data(2), text(1), text color(4), media url(1), PBblock(1), ExtramParams(1), + // sound id(16), sound owner(16) gain (4), flags (1), radius (4) + // jointtype(1) joint pivot(12) joint offset(12) + const int lastzeros = 2 + 1 + 4 + 1 + 1 + 1 + 16 + 16 + 4 + 1 + 4 + 1 + 12 + 12; + zc.AddZeros(lastzeros); + } + + // protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart data, UUID recipientID) protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart part, ScenePresence sp) { @@ -6008,32 +6074,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP //update.JointPivot = Vector3.Zero; //update.JointType = 0; update.Material = part.Material; -/* - if (data.ParentGroup.IsAttachment) - { - update.NameValue - = Util.StringToBytes256( - string.Format("AttachItemID STRING RW SV {0}", data.ParentGroup.FromItemID)); - - update.State = (byte)((data.ParentGroup.AttachmentPoint % 16) * 16 + (data.ParentGroup.AttachmentPoint / 16)); - -// m_log.DebugFormat( -// "[LLCLIENTVIEW]: Sending NameValue {0} for {1} {2} to {3}", -// Util.UTF8.GetString(update.NameValue), data.Name, data.LocalId, Name); -// -// m_log.DebugFormat( -// "[LLCLIENTVIEW]: Sending state {0} for {1} {2} to {3}", -// update.State, data.Name, data.LocalId, Name); - } - else - { - update.NameValue = Utils.EmptyBytes; - - // The root part state is the canonical state for all parts of the object. The other part states in the - // case for attachments may contain conflicting values that can end up crashing the viewer. - update.State = data.ParentGroup.RootPart.Shape.State; - } -*/ if (part.ParentGroup.IsAttachment) { diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs new file mode 100644 index 0000000000..4841adafd2 --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs @@ -0,0 +1,279 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using OpenSim.Framework; +using Nini.Config; +using OpenMetaverse; + +namespace OpenSim.Region.ClientStack.LindenUDP +{ + public sealed class LLUDPZeroEncoder + { + private byte[] m_tmp = new byte[16]; + private byte[] m_dest; + private int zerocount; + private int pos; + + public LLUDPZeroEncoder() + { + } + + public LLUDPZeroEncoder(byte[] data) + { + m_dest = data; + zerocount = 0; + } + + public byte[] Data + { + get + { + return m_dest; + } + set + { + m_dest = value; + } + } + + public int ZeroCount + { + get + { + return zerocount; + } + set + { + zerocount = value; + } + } + + public int Position + { + get + { + return pos; + } + set + { + pos = value; + } + } + + public unsafe void AddZeros(int len) + { + zerocount += len; + while (zerocount > 255) + { + m_dest[pos++] = 0x00; + m_dest[pos++] = 0xff; + zerocount -= 256; + } + } + + public unsafe int Finish() + { + if(zerocount > 0) + { + m_dest[pos++] = 0x00; + m_dest[pos++] = (byte)zerocount; + } + return pos; + } + + public unsafe void AddBytes(byte[] src, int srclen) + { + for (int i = 0; i < srclen; ++i) + { + if (src[i] == 0x00) + { + zerocount++; + if (zerocount == 0) + { + m_dest[pos++] = 0x00; + m_dest[pos++] = 0xff; + zerocount++; + } + } + else + { + if (zerocount != 0) + { + m_dest[pos++] = 0x00; + m_dest[pos++] = (byte)zerocount; + zerocount = 0; + } + + m_dest[pos++] = src[i]; + } + } + } + + public void AddByte(byte v) + { + if (v == 0x00) + { + zerocount++; + if (zerocount == 0) + { + m_dest[pos++] = 0x00; + m_dest[pos++] = 0xff; + zerocount++; + } + } + else + { + if (zerocount != 0) + { + m_dest[pos++] = 0x00; + m_dest[pos++] = (byte)zerocount; + zerocount = 0; + } + + m_dest[pos++] = v; + } + } + + public void AddInt16(short v) + { + if (v == 0) + AddZeros(2); + else + { + Utils.Int16ToBytes(v, m_tmp, 0); + AddBytes(m_tmp, 2); + } + } + + public void AddUInt16(ushort v) + { + if (v == 0) + AddZeros(2); + else + { + Utils.UInt16ToBytes(v, m_tmp, 0); + AddBytes(m_tmp, 2); + } + } + + public void AddInt(int v) + { + if (v == 0) + AddZeros(4); + else + { + Utils.IntToBytesSafepos(v, m_tmp, 0); + AddBytes(m_tmp, 4); + } + } + + public unsafe void AddUInt(uint v) + { + if (v == 0) + AddZeros(4); + else + { + Utils.UIntToBytesSafepos(v, m_tmp, 0); + AddBytes(m_tmp, 4); + } + } + + public void AddFloatToUInt16(float v, float range) + { + Utils.FloatToUInt16Bytes(v, range, m_tmp, 0); + AddBytes(m_tmp, 2); + } + + public void AddFloat(float v) + { + if (v == 0f) + AddZeros(4); + else + { + Utils.FloatToBytesSafepos(v, m_tmp, 0); + AddBytes(m_tmp, 4); + } + } + + public void AddInt64(long v) + { + if (v == 0) + AddZeros(8); + else + { + Utils.Int64ToBytesSafepos(v, m_tmp, 0); + AddBytes(m_tmp, 8); + } + } + + public void AddUInt64(ulong v) + { + if (v == 0) + AddZeros(8); + else + { + Utils.UInt64ToBytesSafepos(v, m_tmp, 0); + AddBytes(m_tmp, 8); + } + } + + public void AddVector3(Vector3 v) + { + if (v == Vector3.Zero) + AddZeros(12); + else + { + v.ToBytes(m_tmp, 0); + AddBytes(m_tmp, 12); + } + } + + public void AddVector4(Vector4 v) + { + if (v == Vector4.Zero) + AddZeros(16); + else + { + v.ToBytes(m_tmp, 0); + AddBytes(m_tmp, 16); + } + } + + public void AddNormQuat(Quaternion v) + { + v.ToBytes(m_tmp, 0); + AddBytes(m_tmp, 12); + } + + public void AddUUID(UUID v) + { + v.ToBytes(m_tmp, 0); + AddBytes(m_tmp, 16); + } + } +} From 0970dc04e2ed5482de4be589535d9eda522d3e0c Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 6 Mar 2019 22:42:37 +0000 Subject: [PATCH 46/62] llupd add direct encode, with inline zeroencode, of objects update ( code path currently not in use) --- .../ClientStack/Linden/UDP/LLClientView.cs | 265 +++++++++++++++--- .../Linden/UDP/LLUDPZeroEncoder.cs | 2 +- 2 files changed, 230 insertions(+), 37 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index dd06e402c8..4158adcdd6 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -3884,44 +3884,27 @@ namespace OpenSim.Region.ClientStack.LindenUDP /// public void SendEntityFullUpdateImmediate(ISceneEntity ent) { -// m_log.DebugFormat( -// "[LLCLIENTVIEW]: Sending immediate object update for avatar {0} {1} to {2} {3}", -// avatar.Name, avatar.UUID, Name, AgentId); - - if (ent == null) + if (ent == null || (!(ent is ScenePresence) && !(ent is SceneObjectPart))) return; + UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + Buffer.BlockCopy(objectUpdateHeader, 0, buf.Data, 0, 7); + + LLUDPZeroEncoder zc = new LLUDPZeroEncoder(buf.Data); + zc.Position = 7; + + zc.AddUInt64(m_scene.RegionInfo.RegionHandle); + zc.AddUInt16(Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f)); + + zc.AddByte(1); // block count + if (ent is ScenePresence) - { - ScenePresence presence = ent as ScenePresence; - UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + CreateAvatarUpdateBlock(ent as ScenePresence, zc); + else + CreatePrimUpdateBlock(ent as SceneObjectPart, (ScenePresence)SceneAgent, zc); - Buffer.BlockCopy(objectUpdateHeader, 0, buf.Data, 0, 7); - - LLUDPZeroEncoder zc = new LLUDPZeroEncoder(buf.Data); - zc.Position = 7; - zc.AddUInt64(m_scene.RegionInfo.RegionHandle); - zc.AddUInt16(Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f)); - zc.AddByte(1); // block count - CreateAvatarUpdateBlock(presence, zc); - buf.DataLength = zc.Finish(); - m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority, null, false, false); - } - else if(ent is SceneObjectPart) - { - ObjectUpdatePacket objupdate = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); - objupdate.RegionData.TimeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); - objupdate.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[1]; - - SceneObjectPart part = ent as SceneObjectPart; - objupdate.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; - objupdate.ObjectData[0] = CreatePrimUpdateBlock(part, (ScenePresence)SceneAgent); - - OutPacket(objupdate, ThrottleOutPacketType.Task); - } - - // We need to record the avatar local id since the root prim of an attachment points to this. - // m_attachmentsSent.Add(avatar.LocalId); + buf.DataLength = zc.Finish(); + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority, null, false, false); } public void SendEntityTerseUpdateImmediate(ISceneEntity ent) @@ -6050,8 +6033,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP zc.AddZeros(lastzeros); } - - // protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart data, UUID recipientID) protected ObjectUpdatePacket.ObjectDataBlock CreatePrimUpdateBlock(SceneObjectPart part, ScenePresence sp) { byte[] objectData = new byte[60]; @@ -6193,6 +6174,218 @@ namespace OpenSim.Region.ClientStack.LindenUDP return update; } + protected void CreatePrimUpdateBlock(SceneObjectPart part, ScenePresence sp, LLUDPZeroEncoder zc) + { + // prepare data + + //NameValue and state + byte[] nv = null; + byte state; + if (part.ParentGroup.IsAttachment) + { + if (part.IsRoot) + nv = Util.StringToBytes256("AttachItemID STRING RW SV " + part.ParentGroup.FromItemID); + + int st = (int)part.ParentGroup.AttachmentPoint; + state = (byte)(((st & 0xf0) >> 4) + ((st & 0x0f) << 4)); ; + } + else + state = part.Shape.State; // not sure about this + + #region PrimFlags + // prim/update flags + PrimFlags primflags = (PrimFlags)m_scene.Permissions.GenerateClientFlags(part, sp); + // Don't send the CreateSelected flag to everyone + primflags &= ~PrimFlags.CreateSelected; + if (sp.UUID == part.OwnerID) + { + if (part.CreateSelected) + { + // Only send this flag once, then unset it + primflags |= PrimFlags.CreateSelected; + part.CreateSelected = false; + } + } + #endregion PrimFlags + + // filter out mesh faces hack + ushort profileBegin = part.Shape.ProfileBegin; + ushort profileHollow = part.Shape.ProfileHollow; + byte profileCurve = part.Shape.ProfileCurve; + byte pathScaleY = part.Shape.PathScaleY; + + if (part.Shape.SculptType == (byte)SculptType.Mesh) // filter out hack + { + profileCurve = (byte)(part.Shape.ProfileCurve & 0x0f); + // fix old values that confused viewers + if (profileBegin == 1) + profileBegin = 9375; + if (profileHollow == 1) + profileHollow = 27500; + // fix torus hole size Y that also confuse some viewers + if (profileCurve == (byte)ProfileShape.Circle && pathScaleY < 150) + pathScaleY = 150; + } + + // data block + byte[] data = null; + switch ((PCode)part.Shape.PCode) + { + case PCode.Grass: + case PCode.Tree: + case PCode.NewTree: + data = new byte[] { part.Shape.State }; + break; + default: + break; + } + + // do encode the things + zc.AddUInt(part.LocalId); + zc.AddByte(state); // state + zc.AddUUID(part.UUID); + zc.AddZeros(4); // crc unused + zc.AddByte(part.Shape.PCode); + zc.AddByte(part.Material); + zc.AddByte(part.ClickAction); // clickaction + zc.AddVector3(part.Shape.Scale); + + // objectdata block + zc.AddByte(60); // fixed object block size + zc.AddVector3(part.RelativePosition); + zc.AddVector3(part.Velocity); + zc.AddVector3(part.Acceleration); + Quaternion rotation = part.RotationOffset; + rotation.Normalize(); + zc.AddNormQuat(rotation); + zc.AddVector3(part.AngularVelocity); + + zc.AddUInt(part.ParentID); + zc.AddUInt((uint)primflags); //update flags + + //pbs + zc.AddByte(part.Shape.PathCurve); + zc.AddByte(profileCurve); + zc.AddUInt16(part.Shape.PathBegin); + zc.AddUInt16(part.Shape.PathEnd); + zc.AddByte(part.Shape.PathScaleX); + zc.AddByte(pathScaleY); + zc.AddByte(part.Shape.PathShearX); + zc.AddByte(part.Shape.PathShearY); + zc.AddByte((byte)part.Shape.PathTwist); + zc.AddByte((byte)part.Shape.PathTwistBegin); + zc.AddByte((byte)part.Shape.PathRadiusOffset); + zc.AddByte((byte)part.Shape.PathTaperX); + zc.AddByte((byte)part.Shape.PathTaperY); + zc.AddByte(part.Shape.PathRevolutions); + zc.AddByte((byte)part.Shape.PathSkew); + zc.AddUInt16(profileBegin); + zc.AddUInt16(part.Shape.ProfileEnd); + zc.AddUInt16(profileHollow); + + // texture + byte[] tentry = part.Shape.TextureEntry; + if (tentry == null) + zc.AddZeros(2); + else + { + int len = tentry.Length; + zc.AddUInt((ushort)len); + zc.AddBytes(tentry, len); + } + + // texture animation + byte[] tanim = part.TextureAnimation; + if (tanim == null) + zc.AddZeros(1); + else + { + int len = tanim.Length; + zc.AddByte((byte)len); + zc.AddBytes(tanim, len); + } + + //NameValue + if(nv == null) + zc.AddZeros(2); + else + { + int len = nv.Length; + zc.AddByte((byte)len); + zc.AddByte((byte)(len >> 8)); + zc.AddBytes(nv, len); + } + + // data + if (data == null) + zc.AddZeros(2); + else + { + int len = data.Length; + zc.AddByte((byte)len); + zc.AddByte((byte)(len >> 8)); + zc.AddBytes(data, len); + } + + //text + if (part.Text.Length == 0) + zc.AddZeros(1); + else + { + byte[] tbuf = Util.StringToBytes(part.Text, 255); + int len = tbuf.Length; + zc.AddByte((byte)len); + zc.AddBytes(tbuf, len); + } + + //textcolor + byte[] tc = part.GetTextColor().GetBytes(false); + zc.AddBytes(tc, 4); + + //media url + if (part.MediaUrl.Length == 0) + zc.AddZeros(1); + else + { + byte[] tbuf = Util.StringToBytes(part.MediaUrl, 255); + int len = tbuf.Length; + zc.AddByte((byte)len); + zc.AddBytes(tbuf, len); + } + + //particle system + byte[] ps = part.ParticleSystem; + if (ps == null) + zc.AddZeros(1); + else + { + int len = ps.Length; + zc.AddByte((byte)len); + zc.AddBytes(ps, len); + } + + //Extraparams + byte[] ep = part.Shape.ExtraParams; + if (ep == null) + zc.AddZeros(1); + else + { + int len = ep.Length; + zc.AddByte((byte)len); + zc.AddBytes(ep, len); + } + + zc.AddUUID(part.Sound); + zc.AddUUID(part.OwnerID); + zc.AddFloat((float)part.SoundGain); + zc.AddByte(part.SoundFlags); + zc.AddFloat((float)part.SoundRadius); + + // jointtype(1) joint pivot(12) joint offset(12) + const int lastzeros = 1 + 12 + 12; + zc.AddZeros(lastzeros); + } + protected ObjectUpdateCompressedPacket.ObjectDataBlock CreateCompressedUpdateBlock(SceneObjectPart part, PrimUpdateFlags updateFlags) { // TODO: Implement this diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs index 4841adafd2..8ed2cf12cd 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPZeroEncoder.cs @@ -134,7 +134,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } } - public void AddByte(byte v) + public unsafe void AddByte(byte v) { if (v == 0x00) { From fdb1ce827bc2058b294fadd4cd76f43fd23e8bd7 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Wed, 6 Mar 2019 22:48:00 +0000 Subject: [PATCH 47/62] fix packet type --- OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 4158adcdd6..c049cc81e6 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -3898,13 +3898,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP zc.AddByte(1); // block count + ThrottleOutPacketType ptype = ThrottleOutPacketType.Task; if (ent is ScenePresence) + { CreateAvatarUpdateBlock(ent as ScenePresence, zc); + ptype |= ThrottleOutPacketType.HighPriority; + } else CreatePrimUpdateBlock(ent as SceneObjectPart, (ScenePresence)SceneAgent, zc); buf.DataLength = zc.Finish(); - m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority, null, false, false); + m_udpServer.SendUDPPacket(m_udpClient, buf, ptype , null, false, false); } public void SendEntityTerseUpdateImmediate(ISceneEntity ent) From 9487f5cdd37c3d1eae5f183d0952426a4a7eade9 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 7 Mar 2019 04:50:05 +0000 Subject: [PATCH 48/62] don't send irrelevant data --- .../ClientStack/Linden/UDP/LLClientView.cs | 61 +++++++++++-------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index c049cc81e6..691b7bb5a1 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -6008,18 +6008,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP zc.AddZeros(4); //update flags - //pbs - zc.AddByte(16); - zc.AddByte(1); - //Utils.UInt16ToBytes(0, dest, pos); pos += 2; - //Utils.UInt16ToBytes(0, dest, pos); pos += 2; - zc.AddZeros(4); - - zc.AddByte(100); - zc.AddByte(100); - - // rest of pbs is 0 (15), texture entry (2) and texture anim (1) - const int pbszeros = 15 + 2 + 1; + //pbs volume data 23 + //texture entry 2 + //texture anim (1) + const int pbszeros = 23 + 2 + 1; zc.AddZeros(pbszeros); //NameValue @@ -6154,15 +6146,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP #endregion PrimFlags - if (part.Sound != UUID.Zero || part.SoundFlags != 0) + bool hassound = part.Sound != UUID.Zero || part.SoundFlags != 0; + if (hassound) { update.Sound = part.Sound; - update.OwnerID = part.OwnerID; update.Gain = (float)part.SoundGain; update.Radius = (float)part.SoundRadius; update.Flags = part.SoundFlags; } + if(hassound || update.PSBlock.Length > 1) + update.OwnerID = part.OwnerID; + switch ((PCode)part.Shape.PCode) { case PCode.Grass: @@ -6333,18 +6328,18 @@ namespace OpenSim.Region.ClientStack.LindenUDP //text if (part.Text.Length == 0) - zc.AddZeros(1); + zc.AddZeros(5); else { - byte[] tbuf = Util.StringToBytes(part.Text, 255); + byte[] tbuf = Util.StringToBytes(part.Text, 254); int len = tbuf.Length; zc.AddByte((byte)len); zc.AddBytes(tbuf, len); - } - //textcolor - byte[] tc = part.GetTextColor().GetBytes(false); - zc.AddBytes(tc, 4); + //textcolor + byte[] tc = part.GetTextColor().GetBytes(false); + zc.AddBytes(tc, 4); + } //media url if (part.MediaUrl.Length == 0) @@ -6357,6 +6352,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP zc.AddBytes(tbuf, len); } + bool hasps = false; //particle system byte[] ps = part.ParticleSystem; if (ps == null) @@ -6366,6 +6362,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP int len = ps.Length; zc.AddByte((byte)len); zc.AddBytes(ps, len); + hasps = len > 1; } //Extraparams @@ -6379,11 +6376,25 @@ namespace OpenSim.Region.ClientStack.LindenUDP zc.AddBytes(ep, len); } - zc.AddUUID(part.Sound); - zc.AddUUID(part.OwnerID); - zc.AddFloat((float)part.SoundGain); - zc.AddByte(part.SoundFlags); - zc.AddFloat((float)part.SoundRadius); + bool hassound = part.Sound != UUID.Zero || part.SoundFlags != 0; + if (hassound) + zc.AddUUID(part.Sound); + else + zc.AddZeros(16); + + if (hassound || hasps) + zc.AddUUID(part.OwnerID); + else + zc.AddZeros(16); + + if (hassound) + { + zc.AddFloat((float)part.SoundGain); + zc.AddByte(part.SoundFlags); + zc.AddFloat((float)part.SoundRadius); + } + else + zc.AddZeros(9); // jointtype(1) joint pivot(12) joint offset(12) const int lastzeros = 1 + 12 + 12; From 6ae1341c58fea31322cefca327ef672e651a27b1 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 7 Mar 2019 04:53:41 +0000 Subject: [PATCH 49/62] don't read from dbs wrong sound flags --- OpenSim/Data/MySQL/MySQLSimulationData.cs | 5 ++++- OpenSim/Data/PGSQL/PGSQLSimulationData.cs | 5 ++++- OpenSim/Data/SQLite/SQLiteSimulationData.cs | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLSimulationData.cs b/OpenSim/Data/MySQL/MySQLSimulationData.cs index c5d7c47856..3db30d8647 100644 --- a/OpenSim/Data/MySQL/MySQLSimulationData.cs +++ b/OpenSim/Data/MySQL/MySQLSimulationData.cs @@ -1403,7 +1403,10 @@ namespace OpenSim.Data.MySQL prim.Sound = DBGuid.FromDB(row["LoopedSound"].ToString()); prim.SoundGain = (float)(double)row["LoopedSoundGain"]; - prim.SoundFlags = 1; // If it's persisted at all, it's looped + if (prim.Sound != UUID.Zero) + prim.SoundFlags = 1; // If it's persisted at all, it's looped + else + prim.SoundFlags = 0; if (!(row["TextureAnimation"] is DBNull)) prim.TextureAnimation = (byte[])row["TextureAnimation"]; diff --git a/OpenSim/Data/PGSQL/PGSQLSimulationData.cs b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs index 569cc806a0..99ceb914e9 100755 --- a/OpenSim/Data/PGSQL/PGSQLSimulationData.cs +++ b/OpenSim/Data/PGSQL/PGSQLSimulationData.cs @@ -1742,7 +1742,10 @@ namespace OpenSim.Data.PGSQL prim.Sound = new UUID((Guid)primRow["LoopedSound"]); prim.SoundGain = Convert.ToSingle(primRow["LoopedSoundGain"]); - prim.SoundFlags = 1; // If it's persisted at all, it's looped + if (prim.Sound != UUID.Zero) + prim.SoundFlags = 1; // If it's persisted at all, it's looped + else + prim.SoundFlags = 0; if (!(primRow["TextureAnimation"] is DBNull)) prim.TextureAnimation = (Byte[])primRow["TextureAnimation"]; diff --git a/OpenSim/Data/SQLite/SQLiteSimulationData.cs b/OpenSim/Data/SQLite/SQLiteSimulationData.cs index e02ac7df2c..1403a8f8e5 100644 --- a/OpenSim/Data/SQLite/SQLiteSimulationData.cs +++ b/OpenSim/Data/SQLite/SQLiteSimulationData.cs @@ -1742,7 +1742,10 @@ namespace OpenSim.Data.SQLite prim.Sound = new UUID(row["LoopedSound"].ToString()); prim.SoundGain = Convert.ToSingle(row["LoopedSoundGain"]); - prim.SoundFlags = 1; // If it's persisted at all, it's looped + if (prim.Sound != UUID.Zero) + prim.SoundFlags = 1; // If it's persisted at all, it's looped + else + prim.SoundFlags = 0; if (!row.IsNull("TextureAnimation")) prim.TextureAnimation = Convert.FromBase64String(row["TextureAnimation"].ToString()); From fbd741ece37b688d7ec2860f4e4f35767213e09f Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 7 Mar 2019 12:12:11 +0000 Subject: [PATCH 50/62] prims have at most 9 face textures --- .../Framework/Scenes/SceneObjectPart.cs | 4 ++-- .../Materials/MaterialsModule.cs | 4 ++-- .../Shared/Api/Implementation/LSL_Api.cs | 6 +++--- bin/OpenMetaverse.dll | Bin 2179072 -> 2179072 bytes 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index a1296ba96b..312ce26e4a 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -5130,7 +5130,7 @@ namespace OpenSim.Region.Framework.Scenes if (changeFlags == 0) return; - m_shape.TextureEntry = newTex.GetBytes(); + m_shape.TextureEntry = newTex.GetBytes(9); TriggerScriptChangedEvent(changeFlags); ParentGroup.HasGroupChanged = true; ScheduleUpdate(PrimUpdateFlags.Textures); @@ -5159,7 +5159,7 @@ namespace OpenSim.Region.Framework.Scenes if (changeFlags == 0) return; - m_shape.TextureEntry = newTex.GetBytes(); + m_shape.TextureEntry = newTex.GetBytes(9); TriggerScriptChangedEvent(changeFlags); ParentGroup.HasGroupChanged = true; ScheduleUpdate(PrimUpdateFlags.Textures); diff --git a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs index 516f9eb4bd..822439fb5e 100644 --- a/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs +++ b/OpenSim/Region/OptionalModules/Materials/MaterialsModule.cs @@ -323,7 +323,7 @@ namespace OpenSim.Region.OptionalModules.Materials } if(facechanged) - part.Shape.TextureEntry = te.GetBytes(); + part.Shape.TextureEntry = te.GetBytes(9); if(facechanged || partchanged) { @@ -632,7 +632,7 @@ namespace OpenSim.Region.OptionalModules.Materials faceEntry.MaterialID = id; //m_log.DebugFormat("[Materials]: in \"{0}\" {1}, setting material ID for face {2} to {3}", sop.Name, sop.UUID, face, id); // We can't use sop.UpdateTextureEntry(te) because it filters, so do it manually - sop.Shape.TextureEntry = te.GetBytes(); + sop.Shape.TextureEntry = te.GetBytes(9); } if(oldid != UUID.Zero) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 95d7a7a780..ab3562f35b 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -10614,7 +10614,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return false; texface.MaterialID = id; - part.Shape.TextureEntry = tex.GetBytes(); + part.Shape.TextureEntry = tex.GetBytes(9); m_materialsModule.RemoveMaterial(oldid); return true; } @@ -10671,7 +10671,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return false; texface.MaterialID = id; - part.Shape.TextureEntry = tex.GetBytes(); + part.Shape.TextureEntry = tex.GetBytes(9); m_materialsModule.RemoveMaterial(oldid); return true; } @@ -10738,7 +10738,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return false; texface.MaterialID = id; - part.Shape.TextureEntry = tex.GetBytes(); + part.Shape.TextureEntry = tex.GetBytes(9); m_materialsModule.RemoveMaterial(oldid); return true; } diff --git a/bin/OpenMetaverse.dll b/bin/OpenMetaverse.dll index b7ddd0bd185b44ee6a5b07e59e1a9e889aa263fc..02edd6c1d39d0d3968a00111e5789ea8fa49b6eb 100755 GIT binary patch delta 378412 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

bnHTh$hNIPzSc+cH(cNu zp;xPO`1gbBCsScRICdlMQ>Mud%fgLwLxl4{Kau8F@@#`~qEG8(V4u2Zb6vDyF4{dV z+G$*p=x3rMRs*}`;(Oi2w+NeKoo^g2<#cT2V%fqB4~(PbBScEh)2^2HyIMM3wC7y3 zAud`s7i}+2EV|r_t{(Q0Z6p>vE9H!YR~u>8NNm(5&>oGr1;0EI*)e8B{6Tpv+ximD z8`4Bqa38u}VqYPsb)o6>d7L)y9!;v(ta7 z81&ncv2k|RQ+*VruX+rnpL!LgRW--kSuZsJrN0`5GC&=G(x#3@8K^#rGDw|?GFV-J zlB=(w6zaPuL)5>Z3{^izsjA0NhNgOoq)PJFjSL;yrRj;BA75_E)E(9H7obIZ$1IGFg2Mi|FADye3Zi;irB#yh0p`y)4c<#2M!uy2NS9pXdDJ?$Cc8 zEGt^6I*V3@l^ypx;H-MD+&4J;<&Zg}<{zIiyR6&HaA&uf5%WIj8461q|B~bAbzv8# z`{JA*3j0Mf^^C9&yJv@o@f{CnG%>=yP6?(P5%x&ss#CSwx0yVKvmSIf((dr-$CB7! fHiQk{c1gKv@rkfAyg9K4Qk>Q#`vu=OlDz*L0*B}n delta 363295 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 APXGV_ From 9ccc3ed756fae1626d7d9fe374f131424c52cb21 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 7 Mar 2019 14:53:32 +0000 Subject: [PATCH 51/62] make use of direct object update encoding --- .../ClientStack/Linden/UDP/LLClientView.cs | 111 ++++++++++++++---- 1 file changed, 88 insertions(+), 23 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 691b7bb5a1..171f8c61cc 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -4102,7 +4102,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP if (mysp == null) return; - List objectUpdateBlocks = null; // List compressedUpdateBlocks = null; List objectUpdates = null; // List compressedUpdates = null; @@ -4349,22 +4348,17 @@ namespace OpenSim.Region.ClientStack.LindenUDP } else { - ObjectUpdatePacket.ObjectDataBlock ablock; if (update.Entity is ScenePresence) - { - ablock = CreateAvatarUpdateBlock((ScenePresence)update.Entity); - } + maxUpdatesBytes -= 150; // crude estimation else - ablock = CreatePrimUpdateBlock((SceneObjectPart)update.Entity, mysp); - if(objectUpdateBlocks == null) + maxUpdatesBytes -= 300; + + if(objectUpdates == null) { - objectUpdateBlocks = new List(); objectUpdates = new List(); maxUpdatesBytes -= 18; } - objectUpdateBlocks.Add(ablock); objectUpdates.Add(update); - maxUpdatesBytes -= ablock.Length; } #endregion Block Construction @@ -4379,16 +4373,86 @@ namespace OpenSim.Region.ClientStack.LindenUDP timeDilation = Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f); - if (objectUpdateBlocks != null) + if(objectUpdates != null) { - ObjectUpdatePacket packet = (ObjectUpdatePacket)PacketPool.Instance.GetPacket(PacketType.ObjectUpdate); - packet.RegionData.RegionHandle = m_scene.RegionInfo.RegionHandle; - packet.RegionData.TimeDilation = timeDilation; - packet.ObjectData = objectUpdateBlocks.ToArray(); - objectUpdateBlocks.Clear(); + int blocks = objectUpdates.Count; + List tau = new List(30); - OutPacket(packet, ThrottleOutPacketType.Task, true, delegate(OutgoingPacket oPacket) { ResendPrimUpdates(objectUpdates, oPacket); }); + UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + Buffer.BlockCopy(objectUpdateHeader, 0, buf.Data, 0, 7); + + LLUDPZeroEncoder zc = new LLUDPZeroEncoder(buf.Data); + zc.Position = 7; + + zc.AddUInt64(m_scene.RegionInfo.RegionHandle); + zc.AddUInt16(Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f)); + + zc.AddByte(1); // tmp block count + + int countposition = zc.Position - 1; + + int lastpos = 0; + int lastzc = 0; + + int count = 0; + foreach (EntityUpdate eu in objectUpdates) + { + lastpos = zc.Position; + lastzc = zc.ZeroCount; + if (eu.Entity is ScenePresence) + CreateAvatarUpdateBlock((ScenePresence)eu.Entity, zc); + else + CreatePrimUpdateBlock((SceneObjectPart)eu.Entity, mysp, zc); + if (zc.Position < LLUDPServer.MTU - 5) + { + tau.Add(eu); + ++count; + --blocks; + } + else if (blocks > 0) + { + // 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; + // get pending zeros at cut point + if(lastzc > 0) + { + buf.Data[lastpos++] = 0; + buf.Data[lastpos++] = (byte)lastzc; + } + buf.DataLength = lastpos; + + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, + delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false); + + buf = newbuf; + zc.Data = buf.Data; + zc.ZeroCount = 0; + zc.Position = countposition + 1; + // im lazy now, just do last again + if (eu.Entity is ScenePresence) + CreateAvatarUpdateBlock((ScenePresence)eu.Entity, zc); + else + CreatePrimUpdateBlock((SceneObjectPart)eu.Entity, mysp, zc); + + tau = new List(30); + tau.Add(eu); + count = 1; + --blocks; + } + } + + if (count > 0) + { + buf.Data[countposition] = (byte)count; + buf.DataLength = zc.Finish(); + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task, + delegate (OutgoingPacket oPacket) { ResendPrimUpdates(tau, oPacket); }, false, false); + } } + /* if (compressedUpdateBlocks != null) { @@ -6289,7 +6353,8 @@ namespace OpenSim.Region.ClientStack.LindenUDP else { int len = tentry.Length; - zc.AddUInt((ushort)len); + zc.AddByte((byte)len); + zc.AddByte((byte)(len >> 8)); zc.AddBytes(tentry, len); } @@ -6327,7 +6392,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } //text - if (part.Text.Length == 0) + if (part.Text == null || part.Text.Length == 0) zc.AddZeros(5); else { @@ -6342,7 +6407,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP } //media url - if (part.MediaUrl.Length == 0) + if (part.MediaUrl == null || part.MediaUrl.Length == 0) zc.AddZeros(1); else { @@ -6355,19 +6420,19 @@ namespace OpenSim.Region.ClientStack.LindenUDP bool hasps = false; //particle system byte[] ps = part.ParticleSystem; - if (ps == null) + if (ps == null || ps.Length < 1) zc.AddZeros(1); else { int len = ps.Length; zc.AddByte((byte)len); zc.AddBytes(ps, len); - hasps = len > 1; + hasps = true; } //Extraparams byte[] ep = part.Shape.ExtraParams; - if (ep == null) + if (ep == null || ep.Length < 2) zc.AddZeros(1); else { From 4e7cddc6a8a764a76b270dbb7aeb3a16e2bff56c Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 7 Mar 2019 17:11:52 +0000 Subject: [PATCH 52/62] vegetation is special --- .../ClientStack/Linden/UDP/LLClientView.cs | 99 +++++++++++++------ 1 file changed, 71 insertions(+), 28 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 171f8c61cc..f984009f5b 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -6241,20 +6241,6 @@ namespace OpenSim.Region.ClientStack.LindenUDP { // prepare data - //NameValue and state - byte[] nv = null; - byte state; - if (part.ParentGroup.IsAttachment) - { - if (part.IsRoot) - nv = Util.StringToBytes256("AttachItemID STRING RW SV " + part.ParentGroup.FromItemID); - - int st = (int)part.ParentGroup.AttachmentPoint; - state = (byte)(((st & 0xf0) >> 4) + ((st & 0x0f) << 4)); ; - } - else - state = part.Shape.State; // not sure about this - #region PrimFlags // prim/update flags PrimFlags primflags = (PrimFlags)m_scene.Permissions.GenerateClientFlags(part, sp); @@ -6271,6 +6257,76 @@ namespace OpenSim.Region.ClientStack.LindenUDP } #endregion PrimFlags + // data block + byte[] data = null; + byte state = part.Shape.State; + PCode pcode = (PCode)part.Shape.PCode; + + //vegetation is special so just do it inline + if(pcode == PCode.Grass || pcode == PCode.Tree || pcode == PCode.NewTree) + { + zc.AddUInt(part.LocalId); + zc.AddByte(state); // state + zc.AddUUID(part.UUID); + zc.AddZeros(4); // crc unused + zc.AddByte((byte)pcode); + // material 1 + // clickaction 1 + zc.AddZeros(2); + zc.AddVector3(part.Shape.Scale); + + // objectdata block + zc.AddByte(60); // fixed object block size + zc.AddVector3(part.RelativePosition); + zc.AddZeros(24); + Quaternion rot = part.RotationOffset; + rot.Normalize(); + zc.AddNormQuat(rot); + zc.AddZeros(12); + + zc.AddUInt(part.ParentID); + zc.AddUInt((uint)primflags); //update flags + + //pbs volume data 23 + //texture entry 2 + //texture anim 1 + //name value 2 + zc.AddZeros(23 + 2 + 1 + 2); + + //data + zc.AddByte(1); + zc.AddZeros(1); + zc.AddByte(state); + + // text 5 + // media url 1 + // particle system 1 + // Extraparams 1 + // sound id 16 + // ownwer 16 + // sound gain 4 + // sound flags 1 + // sound radius 4 + // jointtype 1 + // joint pivot 12 + // joint offset 12 + zc.AddZeros(5 + 1 + 1 + 1 + 16 + 16 + 4 + 1 + 4 + 1 + 12 + 12); + + return; + } + + //NameValue and state + byte[] nv = null; + + if (part.ParentGroup.IsAttachment) + { + if (part.IsRoot) + nv = Util.StringToBytes256("AttachItemID STRING RW SV " + part.ParentGroup.FromItemID); + + int st = (int)part.ParentGroup.AttachmentPoint; + state = (byte)(((st & 0xf0) >> 4) + ((st & 0x0f) << 4)); ; + } + // filter out mesh faces hack ushort profileBegin = part.Shape.ProfileBegin; ushort profileHollow = part.Shape.ProfileHollow; @@ -6290,25 +6346,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP pathScaleY = 150; } - // data block - byte[] data = null; - switch ((PCode)part.Shape.PCode) - { - case PCode.Grass: - case PCode.Tree: - case PCode.NewTree: - data = new byte[] { part.Shape.State }; - break; - default: - break; - } - // do encode the things zc.AddUInt(part.LocalId); zc.AddByte(state); // state zc.AddUUID(part.UUID); zc.AddZeros(4); // crc unused - zc.AddByte(part.Shape.PCode); + zc.AddByte((byte)pcode); zc.AddByte(part.Material); zc.AddByte(part.ClickAction); // clickaction zc.AddVector3(part.Shape.Scale); From 95c4de614427ca9d6b376408b0b56b6167bb41ce Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 7 Mar 2019 18:20:17 +0000 Subject: [PATCH 53/62] grass even more --- .../ClientStack/Linden/UDP/LLClientView.cs | 45 ++++++++++++++++--- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index f984009f5b..4d62bbe4c5 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -6266,7 +6266,10 @@ namespace OpenSim.Region.ClientStack.LindenUDP if(pcode == PCode.Grass || pcode == PCode.Tree || pcode == PCode.NewTree) { zc.AddUInt(part.LocalId); - zc.AddByte(state); // state + if(pcode == PCode.Grass) + zc.AddByte(state); // state + else + zc.AddZeros(1); zc.AddUUID(part.UUID); zc.AddZeros(4); // crc unused zc.AddByte((byte)pcode); @@ -6278,22 +6281,50 @@ namespace OpenSim.Region.ClientStack.LindenUDP // objectdata block zc.AddByte(60); // fixed object block size zc.AddVector3(part.RelativePosition); - zc.AddZeros(24); - Quaternion rot = part.RotationOffset; - rot.Normalize(); - zc.AddNormQuat(rot); - zc.AddZeros(12); + if (pcode == PCode.Grass) + zc.AddZeros(48); + else + { + zc.AddZeros(24); + Quaternion rot = part.RotationOffset; + rot.Normalize(); + zc.AddNormQuat(rot); + zc.AddZeros(12); + } zc.AddUInt(part.ParentID); zc.AddUInt((uint)primflags); //update flags + if (pcode == PCode.Grass) + { + //pbs volume data 23 + //texture entry 2 + //texture anim 1 + //name value 2 + // data 1 + // text 5 + // media url 1 + // particle system 1 + // Extraparams 1 + // sound id 16 + // ownwer 16 + // sound gain 4 + // sound flags 1 + // sound radius 4 + // jointtype 1 + // joint pivot 12 + // joint offset 12 + zc.AddZeros(23 + 2 + 1 + 2 + 1 + 5 + 1 + 1 + 1 + 16 + 16 + 4 + 1 + 4 + 1 + 12 + 12); + return; + } + //pbs volume data 23 //texture entry 2 //texture anim 1 //name value 2 zc.AddZeros(23 + 2 + 1 + 2); - //data + //data: the tree type zc.AddByte(1); zc.AddZeros(1); zc.AddByte(state); From 155e4994573a1ad12ce75e8363e0e92c675bc0b6 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 7 Mar 2019 18:57:13 +0000 Subject: [PATCH 54/62] well let trees have special state and grass data block again. --- OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 4d62bbe4c5..fb876dea6f 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -6266,10 +6266,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP if(pcode == PCode.Grass || pcode == PCode.Tree || pcode == PCode.NewTree) { zc.AddUInt(part.LocalId); - if(pcode == PCode.Grass) - zc.AddByte(state); // state - else - zc.AddZeros(1); + zc.AddByte(state); // state zc.AddUUID(part.UUID); zc.AddZeros(4); // crc unused zc.AddByte((byte)pcode); @@ -6295,6 +6292,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP zc.AddUInt(part.ParentID); zc.AddUInt((uint)primflags); //update flags + /* if (pcode == PCode.Grass) { //pbs volume data 23 @@ -6317,6 +6315,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP zc.AddZeros(23 + 2 + 1 + 2 + 1 + 5 + 1 + 1 + 1 + 16 + 16 + 4 + 1 + 4 + 1 + 12 + 12); return; } + */ //pbs volume data 23 //texture entry 2 From a32060180fd6e24c7188b24769e294146133d9a0 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 8 Mar 2019 19:14:09 +0000 Subject: [PATCH 55/62] agent animation object ids are only for self avatar --- .../ClientStack/Linden/UDP/LLClientView.cs | 50 +++++++++++++++---- 1 file changed, 40 insertions(+), 10 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index fb876dea6f..6ec87aba42 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -3833,24 +3833,54 @@ namespace OpenSim.Region.ClientStack.LindenUDP AvatarAnimationPacket ani = (AvatarAnimationPacket)PacketPool.Instance.GetPacket(PacketType.AvatarAnimation); // TODO: don't create new blocks if recycling an old packet - ani.AnimationSourceList = new AvatarAnimationPacket.AnimationSourceListBlock[animations.Length]; ani.Sender = new AvatarAnimationPacket.SenderBlock(); ani.Sender.ID = sourceAgentId; ani.AnimationList = new AvatarAnimationPacket.AnimationListBlock[animations.Length]; ani.PhysicalAvatarEventList = new AvatarAnimationPacket.PhysicalAvatarEventListBlock[0]; - for (int i = 0; i < animations.Length; ++i) + //self animations + if (sourceAgentId == AgentId) { - ani.AnimationList[i] = new AvatarAnimationPacket.AnimationListBlock(); - ani.AnimationList[i].AnimID = animations[i]; - ani.AnimationList[i].AnimSequenceID = seqs[i]; + List withobjects = new List(animations.Length); + List noobjects = new List(animations.Length); + for(int i = 0; i < animations.Length; ++i) + { + if(objectIDs[i] == sourceAgentId || objectIDs[i] == UUID.Zero) + noobjects.Add(i); + else + withobjects.Add(i); + } - ani.AnimationSourceList[i] = new AvatarAnimationPacket.AnimationSourceListBlock(); - if (objectIDs[i].Equals(sourceAgentId)) - ani.AnimationSourceList[i].ObjectID = UUID.Zero; - else - ani.AnimationSourceList[i].ObjectID = objectIDs[i]; + ani.AnimationSourceList = new AvatarAnimationPacket.AnimationSourceListBlock[withobjects.Count]; + int k = 0; + foreach (int i in withobjects) + { + ani.AnimationList[k] = new AvatarAnimationPacket.AnimationListBlock(); + ani.AnimationList[k].AnimID = animations[i]; + ani.AnimationList[k].AnimSequenceID = seqs[i]; + ani.AnimationSourceList[k] = new AvatarAnimationPacket.AnimationSourceListBlock(); + ani.AnimationSourceList[k].ObjectID = objectIDs[i]; + k++; + } + foreach (int i in noobjects) + { + ani.AnimationList[k] = new AvatarAnimationPacket.AnimationListBlock(); + ani.AnimationList[k].AnimID = animations[i]; + ani.AnimationList[k].AnimSequenceID = seqs[i]; + k++; + } } + else + { + ani.AnimationSourceList = new AvatarAnimationPacket.AnimationSourceListBlock[0]; + for (int i = 0; i < animations.Length; ++i) + { + ani.AnimationList[i] = new AvatarAnimationPacket.AnimationListBlock(); + ani.AnimationList[i].AnimID = animations[i]; + ani.AnimationList[i].AnimSequenceID = seqs[i]; + } + } + OutPacket(ani, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); } From e9d7ced733e99904de315d71d7b851e8ff7a3a67 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 8 Mar 2019 21:14:08 +0000 Subject: [PATCH 56/62] just direct encode avatar animation also --- .../ClientStack/Linden/UDP/LLClientView.cs | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 6ec87aba42..5d42ecdc5d 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -3827,6 +3827,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(avp, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); } +/* public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) { // m_log.DebugFormat("[LLCLIENTVIEW]: Sending animations for {0} to {1}", sourceAgentId, Name); @@ -3883,6 +3884,79 @@ namespace OpenSim.Region.ClientStack.LindenUDP OutPacket(ani, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority); } +*/ + + static private readonly byte[] AvatarAnimationHeader = new byte[] { + Helpers.MSG_RELIABLE | Helpers.MSG_ZEROCODED, // zero code is not as spec + 0, 0, 0, 0, // sequence number + 0, // extra + 20 // ID (high frequency) + }; + + public void SendAnimations(UUID[] animations, int[] seqs, UUID sourceAgentId, UUID[] objectIDs) + { + // m_log.DebugFormat("[LLCLIENTVIEW]: Sending animations for {0} to {1}", sourceAgentId, Name); + + UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + byte[] data = buf.Data; + //setup header + Buffer.BlockCopy(AvatarAnimationHeader, 0, data, 0, 7); + //agent block + sourceAgentId.ToBytes(data, 7); + + // animations count + data[23] = (byte)animations.Length; + + int pos = 24; + + //self animations + if (sourceAgentId == AgentId) + { + List withobjects = new List(animations.Length); + List noobjects = new List(animations.Length); + for (int i = 0; i < animations.Length; ++i) + { + if (objectIDs[i] == sourceAgentId || objectIDs[i] == UUID.Zero) + noobjects.Add(i); + else + withobjects.Add(i); + } + + // first the ones with corresponding objects + foreach (int i in withobjects) + { + animations[i].ToBytes(data, pos); pos += 16; + Utils.IntToBytesSafepos(seqs[i], data, pos); pos += 4; + } + // then the rest + foreach (int i in noobjects) + { + animations[i].ToBytes(data, pos); pos += 16; + Utils.IntToBytesSafepos(seqs[i], data, pos); pos += 4; + } + // object ids block + data[pos++] = (byte)withobjects.Count; + foreach (int i in withobjects) + { + objectIDs[i].ToBytes(data, pos); pos += 16; + } + } + else + { + for(int i = 0; i < animations.Length; ++i) + { + animations[i].ToBytes(data, pos); pos += 16; + Utils.IntToBytesSafepos(seqs[i], data, pos); pos += 4; + } + data[pos++] = 0; // no object ids + } + + data[pos++] = 0; // no physical avatar events + + buf.DataLength = pos; + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Task | ThrottleOutPacketType.HighPriority, + null, false, false); + } public void SendObjectAnimations(UUID[] animations, int[] seqs, UUID senderId) { From 6bc37f348e70909bfa91319aefa76e76ffd7b398 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 8 Mar 2019 22:28:57 +0000 Subject: [PATCH 57/62] ooops bad zeroencode flag --- OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 5d42ecdc5d..5353194d18 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -3887,7 +3887,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP */ static private readonly byte[] AvatarAnimationHeader = new byte[] { - Helpers.MSG_RELIABLE | Helpers.MSG_ZEROCODED, // zero code is not as spec + Helpers.MSG_RELIABLE, 0, 0, 0, 0, // sequence number 0, // extra 20 // ID (high frequency) From 343239c7c395bf81cd07057bd89a846d81d263a7 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sat, 9 Mar 2019 12:04:26 +0000 Subject: [PATCH 58/62] do not send animated attachments to viewers that do not support them. --- OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index 5353194d18..ecae8c8c58 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -4285,7 +4285,12 @@ namespace OpenSim.Region.ClientStack.LindenUDP } if (grp.IsAttachment) - { // Someone else's HUD, why are we getting these? + { + // animated attachments are nasty if not supported by viewer + if(!m_SupportObjectAnimations && grp.RootPart.Shape.MeshFlagEntry) + continue; + + // Someone else's HUD, why are we getting these? if (grp.OwnerID != AgentId && grp.HasPrivateAttachmentPoint) continue; From 7b55d42b11d899456d8e5a153fb097e8691b92e8 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sun, 10 Mar 2019 11:23:38 +0000 Subject: [PATCH 59/62] lludp fix max packet size on ack appends --- OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs index 6fd782a174..2300800f57 100755 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLUDPServer.cs @@ -256,6 +256,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP ///

Maximum transmission unit, or UDP packet size, for the LLUDP protocol public const int MTU = 1400; + public const int MAXPAYLOAD = 1250; /// Number of forced client logouts due to no receipt of packets before timeout. public int ClientLogoutsDueToNoReceives { get; protected set; } @@ -1150,7 +1151,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP // no more ACKs to append int ackCount = 0; uint ack; - while (dataLength + 5 < buffer.Data.Length && ackCount < 256 && udpClient.PendingAcks.Dequeue(out ack)) + while (dataLength + 5 < MTU && ackCount < 256 && udpClient.PendingAcks.Dequeue(out ack)) { Utils.UIntToBytesBig(ack, buffer.Data, dataLength); dataLength += 4; From 32a03a49fc06edb65cb016b8c69f88e3c595be1d Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Sun, 10 Mar 2019 15:56:14 +0000 Subject: [PATCH 60/62] lludp: direct encode terrain patchs packets --- .../ClientStack/Linden/UDP/LLClientView.cs | 86 +++++++++++++++++-- .../Framework/Scenes/TerrainCompressor.cs | 2 + 2 files changed, 83 insertions(+), 5 deletions(-) diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs index ecae8c8c58..ac041f500b 100644 --- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs +++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs @@ -1257,6 +1257,16 @@ namespace OpenSim.Region.ClientStack.LindenUDP SendLayerTopRight(x1 + 1, y1, x2, y2 - 1); } + static private readonly byte[] TerrainPacketHeader = new byte[] { + Helpers.MSG_RELIABLE, // zero code is not as spec + 0, 0, 0, 0, // sequence number + 0, // extra + 11, // ID (high frequency) + }; + + private const int END_OF_PATCHES = 97; + private const int STRIDE = 264; + public void SendLayerData(int[] map) { if(map == null) @@ -1264,9 +1274,75 @@ namespace OpenSim.Region.ClientStack.LindenUDP try { - List packets = OpenSimTerrainCompressor.CreateLayerDataPackets(m_scene.Heightmap.GetTerrainData(), map); - foreach (LayerDataPacket pkt in packets) - OutPacket(pkt, ThrottleOutPacketType.Land); + TerrainData terrData = m_scene.Heightmap.GetTerrainData(); + byte landPacketType = (terrData.SizeX > Constants.RegionSize || terrData.SizeY > Constants.RegionSize) ? + (byte)TerrainPatch.LayerType.LandExtended : (byte)TerrainPatch.LayerType.Land; + + int numberPatchs = map.Length / 2; + + UDPPacketBuffer buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + byte[] data = buf.Data; + + Buffer.BlockCopy(TerrainPacketHeader, 0, data, 0, 7); + + data[7] = landPacketType; + //data[8] and data[9] == datablock size to fill later + + data[10] = 0; // BitPack needs this on reused packets + + // start data + BitPack bitpack = new BitPack(data, 10); + bitpack.PackBits(STRIDE, 16); + bitpack.PackBitsFromByte(16); + bitpack.PackBitsFromByte(landPacketType); + + int s; + int datasize = 0; + for (int i = 0; i < numberPatchs; i++) + { + s = 2 * i; + OpenSimTerrainCompressor.CreatePatchFromTerrainData(bitpack, terrData, map[s], map[s + 1]); + if (bitpack.BytePos > 950 && i != numberPatchs - 1) + { + //finish this packet + bitpack.PackBitsFromByte(END_OF_PATCHES); + + // fix the datablock lenght + datasize = bitpack.BytePos - 9; + data[8] = (byte)datasize; + data[9] = (byte)(datasize >> 8); + + buf.DataLength = bitpack.BytePos + 1; + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Land, null, false, false); + + // start another + buf = m_udpServer.GetNewUDPBuffer(m_udpClient.RemoteEndPoint); + data = buf.Data; + + Buffer.BlockCopy(TerrainPacketHeader, 0, data, 0, 7); + + data[7] = landPacketType; + //data[8] and data[9] == datablock size to fill later + + data[10] = 0; // BitPack needs this + // start data + bitpack = new BitPack(data, 10); + + bitpack.PackBits(STRIDE, 16); + bitpack.PackBitsFromByte(16); + bitpack.PackBitsFromByte(landPacketType); + } + } + + bitpack.PackBitsFromByte(END_OF_PATCHES); + + datasize = bitpack.BytePos - 9; + data[8] = (byte)datasize; + data[9] = (byte)(datasize >> 8); + + buf.DataLength = bitpack.BytePos + 1; + m_udpServer.SendUDPPacket(m_udpClient, buf, ThrottleOutPacketType.Land, null, false, false); + } catch (Exception e) { @@ -4512,7 +4588,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP CreateAvatarUpdateBlock((ScenePresence)eu.Entity, zc); else CreatePrimUpdateBlock((SceneObjectPart)eu.Entity, mysp, zc); - if (zc.Position < LLUDPServer.MTU - 5) + if (zc.Position < LLUDPServer.MAXPAYLOAD) { tau.Add(eu); ++count; @@ -4593,7 +4669,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP { lastpos = pos; CreateImprovedTerseBlock(eu.Entity, buf.Data, ref pos, (eu.Flags & PrimUpdateFlags.Textures) != 0); - if (pos < LLUDPServer.MTU) + if (pos < LLUDPServer.MAXPAYLOAD) { tau.Add(eu); ++count; diff --git a/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs b/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs index a60381afb7..020c7bed99 100644 --- a/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs +++ b/OpenSim/Region/Framework/Scenes/TerrainCompressor.cs @@ -155,6 +155,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return iout; } +/* // new using terrain data and patchs indexes public static List CreateLayerDataPackets(TerrainData terrData, int[] map) { @@ -213,6 +214,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP return ret; } +*/ public static void CreatePatchFromTerrainData(BitPack output, TerrainData terrData, int patchX, int patchY) { From 186e9e2838018affd82d62444e7fdfb08400960f Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Mon, 11 Mar 2019 23:15:30 +0000 Subject: [PATCH 61/62] at login do try proper region name match before sending just somewhere that looks similar --- OpenSim/Services/LLLoginService/LLLoginService.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 0438673ce3..3a6f41b800 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -757,6 +757,14 @@ namespace OpenSim.Services.LLLoginService } } } + + //find a exact match + foreach(GridRegion r in regions) + { + if(string.Equals(regionName, r.RegionName, StringComparison.CurrentCultureIgnoreCase)) + return r; + } + // else, whatever return regions[0]; } else From 68b0d9f31dd44eb4e24bf5c60710739706b61199 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Mon, 11 Mar 2019 23:24:20 +0000 Subject: [PATCH 62/62] errr use invariantculture compare not current --- OpenSim/Services/LLLoginService/LLLoginService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSim/Services/LLLoginService/LLLoginService.cs b/OpenSim/Services/LLLoginService/LLLoginService.cs index 3a6f41b800..22748cc1f7 100644 --- a/OpenSim/Services/LLLoginService/LLLoginService.cs +++ b/OpenSim/Services/LLLoginService/LLLoginService.cs @@ -761,7 +761,7 @@ namespace OpenSim.Services.LLLoginService //find a exact match foreach(GridRegion r in regions) { - if(string.Equals(regionName, r.RegionName, StringComparison.CurrentCultureIgnoreCase)) + if(string.Equals(regionName, r.RegionName, StringComparison.InvariantCultureIgnoreCase)) return r; } // else, whatever