From 573af89389ce0539b98f0ea44c0e3bc303760a19 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Thu, 28 Mar 2024 22:03:14 +0000 Subject: [PATCH] store linksetdata in inventory and region mysql db ( others todo) UNTESTED --- OpenSim/Data/MySQL/MySQLSimulationData.cs | 63 ++++---- .../MySQL/Resources/RegionStore.migrations | 5 + .../Region/Framework/Scenes/LinksetData.cs | 134 +++++++++++++++++- .../Serialization/SceneObjectSerializer.cs | 16 ++- 4 files changed, 184 insertions(+), 34 deletions(-) diff --git a/OpenSim/Data/MySQL/MySQLSimulationData.cs b/OpenSim/Data/MySQL/MySQLSimulationData.cs index 8b24cdfa52..5e1cb72870 100644 --- a/OpenSim/Data/MySQL/MySQLSimulationData.cs +++ b/OpenSim/Data/MySQL/MySQLSimulationData.cs @@ -170,7 +170,8 @@ namespace OpenSim.Data.MySQL "AttachedPosY, AttachedPosZ, " + "PhysicsShapeType, Density, GravityModifier, " + "Friction, Restitution, Vehicle, PhysInertia, DynAttrs, " + - "RotationAxisLocks, sopanims, sitactrange, pseudocrc" + + "RotationAxisLocks, sopanims, sitactrange, pseudocrc, " + + "lnkstBinData" + ") values (" + "?UUID, " + "?CreationDate, ?Name, ?Text, " + "?Description, ?SitName, ?TouchName, " + @@ -204,8 +205,9 @@ namespace OpenSim.Data.MySQL "?LinkNumber, ?MediaURL, ?KeyframeMotion, ?AttachedPosX, " + "?AttachedPosY, ?AttachedPosZ, " + "?PhysicsShapeType, ?Density, ?GravityModifier, " + - "?Friction, ?Restitution, ?Vehicle, ?PhysInertia, ?DynAttrs," + - "?RotationAxisLocks, ?sopanims, ?sitactrange, ?pseudocrc)"; + "?Friction, ?Restitution, ?Vehicle, ?PhysInertia, ?DynAttrs, " + + "?RotationAxisLocks, ?sopanims, ?sitactrange, ?pseudocrc, " + + "?lnkstBinData)"; FillPrimCommand(cmd, prim, obj.UUID, regionUUID); @@ -344,12 +346,27 @@ namespace OpenSim.Data.MySQL else prim.Shape = BuildShape(reader); - UUID parentID = DBGuid.FromDB(reader["SceneGroupID"].ToString()); - if (parentID != prim.UUID) - prim.ParentUUID = parentID; - prims[prim.UUID] = prim; + UUID parentID = DBGuid.FromDB(reader["SceneGroupID"].ToString()); + if (parentID.NotEqual(prim.UUID)) + prim.ParentUUID = parentID; + else + { + //create linkset and extract its data stored on root + SceneObjectGroup newSog = new SceneObjectGroup(prim); + if(objects.TryAdd(prim.UUID, newSog)) + { + if(reader["lnkstBinData"] is not DBNull) + { + byte[] data = (byte[])reader["lnkstBinData"]; + newSog.LinksetData = LinksetData.FromBin(data); + } + } + else + m_log.Warn($"[REGION DB]: duplicated SOG with root prim \"{prim.Name}\" {prim.UUID} in region {regionID}"); + } + ++count; if (count % ROWS_PER_QUERY == 0) m_log.Debug("[REGION DB]: Loaded " + count + " prims..."); @@ -362,24 +379,12 @@ namespace OpenSim.Data.MySQL #endregion Prim Loading - #region SceneObjectGroup Creation - - // Create all of the SOGs from the root prims first - foreach (SceneObjectPart prim in prims.Values) - { - if (prim.ParentUUID.IsZero()) - { - objects[prim.UUID] = new SceneObjectGroup(prim); - } - } - // Add all of the children objects to the SOGs foreach (SceneObjectPart prim in prims.Values) { - SceneObjectGroup sog; - if (prim.UUID != prim.ParentUUID) + if (prim.UUID.NotEqual(prim.ParentUUID)) { - if (objects.TryGetValue(prim.ParentUUID, out sog)) + if (objects.TryGetValue(prim.ParentUUID, out SceneObjectGroup sog)) { int originalLinkNum = prim.LinkNum; @@ -392,16 +397,13 @@ namespace OpenSim.Data.MySQL } else { - m_log.WarnFormat( - "[REGION DB]: Database contains an orphan child prim {0} {1} in region {2} pointing to missing parent {3}. This prim will not be loaded.", - prim.Name, prim.UUID, regionID, prim.ParentUUID); + m_log.Warn( + $"[REGION DB]: orphan child prim {prim.Name} {prim.UUID} in region {regionID} with missing parent {prim.ParentUUID}. Prim not loaded."); } } } - #endregion SceneObjectGroup Creation - - m_log.DebugFormat("[REGION DB]: Loaded {0} objects using {1} prims", objects.Count, prims.Count); + m_log.Debug($"[REGION DB]: Loaded {objects.Count} objects using {prims.Count} prims"); #region Prim Inventory Loading @@ -1188,7 +1190,7 @@ namespace OpenSim.Data.MySQL int pseudocrc = (int)row["pseudocrc"]; if(pseudocrc != 0) prim.PseudoCRC = pseudocrc; - + return prim; } @@ -1609,6 +1611,11 @@ namespace OpenSim.Data.MySQL cmd.Parameters.AddWithValue("sitactrange", prim.SitActiveRange); cmd.Parameters.AddWithValue("pseudocrc", prim.PseudoCRC); + + if(prim.IsRoot && prim.ParentGroup.LinksetData is not null) + cmd.Parameters.AddWithValue("lnkstBinData", prim.ParentGroup.LinksetData.ToBin()); + else + cmd.Parameters.AddWithValue("lnkstBinData", null); } /// diff --git a/OpenSim/Data/MySQL/Resources/RegionStore.migrations b/OpenSim/Data/MySQL/Resources/RegionStore.migrations index 30407aff1d..142916551c 100644 --- a/OpenSim/Data/MySQL/Resources/RegionStore.migrations +++ b/OpenSim/Data/MySQL/Resources/RegionStore.migrations @@ -553,3 +553,8 @@ COMMIT; :VERSION 64 #----- material overrides ALTER TABLE `primshapes` ADD COLUMN `MatOvrd` blob default NULL; COMMIT; + +:VERSION 65 #----- add linkset data binary storage column +BEGIN; +ALTER TABLE `prims` ADD COLUMN `lnkstBinData` blob default NULL; +COMMIT; \ No newline at end of file diff --git a/OpenSim/Region/Framework/Scenes/LinksetData.cs b/OpenSim/Region/Framework/Scenes/LinksetData.cs index f450fc2a8f..b4d3c6a7be 100644 --- a/OpenSim/Region/Framework/Scenes/LinksetData.cs +++ b/OpenSim/Region/Framework/Scenes/LinksetData.cs @@ -26,11 +26,15 @@ */ using System; +using System.Buffers; using System.Collections.Generic; +using System.IO; using System.Runtime.CompilerServices; -//using log4net; using System.Text.Json; using System.Text.RegularExpressions; +using System.Xml; + +//using log4net; namespace OpenSim.Region.Framework.Scenes { @@ -442,13 +446,139 @@ namespace OpenSim.Region.Framework.Scenes } } - public string SerializeLinksetData() + public string ToJson() { lock (linksetDataLock) { return JsonSerializer.Serialize>(Data); } } + + public void ToXML(XmlTextWriter writer) + { + if (Data.Count < 1) + return; + using MemoryStream ms = new(m_MemoryUsed); + ToBin(ms); + if (ms.Length < 1) + return; + + writer.WriteStartElement("lnkstdt"); + writer.WriteBase64(ms.GetBuffer(), 0, (int)ms.Length); + writer.WriteEndElement(); + } + + public static LinksetData FromXML(ReadOnlySpan data) + { + if (data.Length < 8) + return null; + var minLength = ((data.Length * 3) + 3) / 4; + var bindata = ArrayPool.Shared.Rent(minLength); + try + { + if (Convert.TryFromBase64Chars(data, bindata, out int bytesWritten)) + return FromBin(bindata); + } + catch + { + } + finally + { + ArrayPool.Shared.Return(bindata); + } + return null; + } + + public byte[] ToBin() + { + if(Data.Count < 1) + return null; + + using MemoryStream ms = new(m_MemoryUsed); + ToBin(ms); + return ms.ToArray(); + } + + public void ToBin(MemoryStream ms) + { + try + { + using BinaryWriter bw = new BinaryWriter(ms, System.Text.Encoding.UTF8, true); + bw.Write((byte)1); // storage version + bw.Write7BitEncodedInt(m_MemoryLimit); + lock (linksetDataLock) + { + bw.Write7BitEncodedInt(Data.Count); + foreach (var kvp in Data) + { + bw.Write(kvp.Key); + bw.Write(kvp.Value.Value); + bw.Write(kvp.Value.Password); + } + } + return; + } + catch { } + ms.SetLength(0); + } + public static LinksetData FromBin(byte[] data) + { + if (data.Length < 8) + return null; + + try + { + using BinaryReader br = new BinaryReader(new MemoryStream(data)); + int version = br.Read7BitEncodedInt(); + int memoryLimit = br.Read7BitEncodedInt(); + if(memoryLimit < 0 || memoryLimit > 256 * 1024) + memoryLimit = 256 * 1024; + + int count = br.Read7BitEncodedInt(); + if(count == 0) + return null; + LinksetData ld = new LinksetData(memoryLimit); + for(int i = 0; i < count; i++) + { + string key = br.ReadString(); + if (key.Length == 0) + continue; + ld.m_MemoryUsed += key.Length; + + string value = br.ReadString(); + if(value.Length == 0) + continue; + ld.m_MemoryUsed += value.Length; + + string pass = br.ReadString(); + ld.m_MemoryUsed += pass.Length; + if(ld.m_MemoryUsed > memoryLimit) + break; + + if(pass.Length > 0) + { + ld.Data[key] = new LinksetDataEntry() + { + Value = value, + Password = pass + }; + } + else + { + ld.Data[key] = new LinksetDataEntry() + { + Value = value, + Password = null + }; + } + } + return ld; + } + catch + { + } + return null; + } } public class LinksetDataEntry diff --git a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs index 75449a097c..6e4d021fc3 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs @@ -143,8 +143,12 @@ namespace OpenSim.Region.Framework.Scenes.Serialization string innerkeytxt = reader.ReadElementContentAsString(); sceneObject.RootPart.KeyframeMotion = KeyframeMotion.FromData(sceneObject, Convert.FromBase64String(innerkeytxt)); } - else - sceneObject.RootPart.KeyframeMotion = null; + + if (reader.Name == "lnkstdt" && reader.NodeType == XmlNodeType.Element) + { + string innerlnkstdttxt = reader.ReadElementContentAsString(); + sceneObject.LinksetData = LinksetData.FromXML(innerlnkstdttxt.AsSpan()); + } // Script state may, or may not, exist. Not having any, is NOT // ever a problem. @@ -255,6 +259,8 @@ namespace OpenSim.Region.Framework.Scenes.Serialization writer.WriteEndElement(); } + sceneObject.LinksetData?.ToXML(writer); + if (doScriptStates) sceneObject.SaveScriptedState(writer); @@ -325,8 +331,10 @@ namespace OpenSim.Region.Framework.Scenes.Serialization XmlNodeList keymotion = doc.GetElementsByTagName("KeyframeMotion"); if (keymotion.Count > 0) sceneObject.RootPart.KeyframeMotion = KeyframeMotion.FromData(sceneObject, Convert.FromBase64String(keymotion[0].InnerText)); - else - sceneObject.RootPart.KeyframeMotion = null; + + XmlNodeList keylinksetdata = doc.GetElementsByTagName("lnkstdt"); + if (keylinksetdata.Count > 0) + sceneObject.LinksetData = LinksetData.FromXML(keylinksetdata[0].InnerText.AsSpan()); // Script state may, or may not, exist. Not having any, is NOT // ever a problem.