diff --git a/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs b/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs index bac78270a7..f10e848f68 100644 --- a/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs +++ b/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs @@ -303,15 +303,19 @@ namespace OpenSim.Region.CoreModules.World.Serialiser.Tests { case "UUID": xtr.ReadStartElement("UUID"); - uuid = UUID.Parse(xtr.ReadElementString("Guid")); - xtr.ReadEndElement(); + try + { + uuid = UUID.Parse(xtr.ReadElementString("UUID")); + xtr.ReadEndElement(); + } + catch { } // ignore everything but ... break; case "Name": name = xtr.ReadElementContentAsString(); break; case "CreatorID": xtr.ReadStartElement("CreatorID"); - creatorId = UUID.Parse(xtr.ReadElementString("Guid")); + creatorId = UUID.Parse(xtr.ReadElementString("UUID")); xtr.ReadEndElement(); break; } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 39b109ba38..7a6449decf 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -39,6 +39,7 @@ using OpenMetaverse.Packets; using OpenSim.Framework; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes.Scripting; +using OpenSim.Region.Framework.Scenes.Serialization; using OpenSim.Region.Physics.Manager; namespace OpenSim.Region.Framework.Scenes @@ -118,40 +119,36 @@ namespace OpenSim.Region.Framework.Scenes /// /// Is this sop a root part? /// - [XmlIgnore] + public bool IsRoot { get { return ParentGroup.RootPart == this; } } - // use only one serializer to give the runtime a chance to optimize it (it won't do that if you - // use a new instance every time) - private static XmlSerializer serializer = new XmlSerializer(typeof (SceneObjectPart)); - #region Fields public bool AllowedDrop; - [XmlIgnore] + public bool DIE_AT_EDGE; - [XmlIgnore] + public bool RETURN_AT_EDGE; - [XmlIgnore] + public bool BlockGrab; - [XmlIgnore] + public bool StatusSandbox; - [XmlIgnore] + public Vector3 StatusSandboxPos; // TODO: This needs to be persisted in next XML version update! - [XmlIgnore] + public readonly int[] PayPrice = {-2,-2,-2,-2,-2}; - [XmlIgnore] + public PhysicsActor PhysActor { get { return m_physActor; } @@ -166,43 +163,43 @@ namespace OpenSim.Region.Framework.Scenes // Note: This isn't persisted in the database right now, as the fields for that aren't just there yet. // Not a big problem as long as the script that sets it remains in the prim on startup. // for SL compatibility it should be persisted though (set sound / displaytext / particlesystem, kill script) - [XmlIgnore] + public UUID Sound; - [XmlIgnore] + public byte SoundFlags; - [XmlIgnore] + public double SoundGain; - [XmlIgnore] + public double SoundRadius; - [XmlIgnore] + public uint TimeStampFull; - [XmlIgnore] + public uint TimeStampLastActivity; // Will be used for AutoReturn - [XmlIgnore] + public uint TimeStampTerse; - [XmlIgnore] + public UUID FromItemID; - [XmlIgnore] + public UUID FromFolderID; - [XmlIgnore] + public int STATUS_ROTATE_X; - [XmlIgnore] + public int STATUS_ROTATE_Y; - [XmlIgnore] + public int STATUS_ROTATE_Z; - [XmlIgnore] + private Dictionary m_CollisionFilter = new Dictionary(); /// @@ -211,68 +208,68 @@ namespace OpenSim.Region.Framework.Scenes /// private UUID m_fromUserInventoryItemID; - [XmlIgnore] + public UUID FromUserInventoryItemID { get { return m_fromUserInventoryItemID; } } - [XmlIgnore] + public bool IsAttachment; - [XmlIgnore] + public scriptEvents AggregateScriptEvents; - [XmlIgnore] + public UUID AttachedAvatar; - [XmlIgnore] + public Vector3 AttachedPos; - [XmlIgnore] + public uint AttachmentPoint; - [XmlIgnore] + public Vector3 RotationAxis = Vector3.One; - [XmlIgnore] + public bool VolumeDetectActive; // XmlIgnore set to avoid problems with persistance until I come to care for this // Certainly this must be a persistant setting finally - [XmlIgnore] + public bool IsWaitingForFirstSpinUpdatePacket; - [XmlIgnore] + public Quaternion SpinOldOrientation = Quaternion.Identity; - [XmlIgnore] + public Quaternion m_APIDTarget = Quaternion.Identity; - [XmlIgnore] + public float m_APIDDamp = 0; - [XmlIgnore] + public float m_APIDStrength = 0; /// /// This part's inventory /// - [XmlIgnore] + public IEntityInventory Inventory { get { return m_inventory; } } protected SceneObjectPartInventory m_inventory; - [XmlIgnore] + public bool Undoing; - [XmlIgnore] + public bool IgnoreUndoUpdate = false; - [XmlIgnore] + private PrimFlags LocalFlags; - [XmlIgnore] + private float m_damage = -1.0f; private byte[] m_TextureAnimation; private byte m_clickAction; @@ -280,9 +277,9 @@ namespace OpenSim.Region.Framework.Scenes private string m_description = String.Empty; private readonly List m_lastColliders = new List(); private int m_linkNum; - [XmlIgnore] + private int m_scriptAccessPin; - [XmlIgnore] + private readonly Dictionary m_scriptEvents = new Dictionary(); private string m_sitName = String.Empty; private Quaternion m_sitTargetOrientation = Quaternion.Identity; @@ -548,7 +545,7 @@ namespace OpenSim.Region.Framework.Scenes } - [XmlIgnore] + public Dictionary CollisionFilter { get { return m_CollisionFilter; } @@ -558,21 +555,21 @@ namespace OpenSim.Region.Framework.Scenes } } - [XmlIgnore] + public Quaternion APIDTarget { get { return m_APIDTarget; } set { m_APIDTarget = value; } } - [XmlIgnore] + public float APIDDamp { get { return m_APIDDamp; } set { m_APIDDamp = value; } } - [XmlIgnore] + public float APIDStrength { get { return m_APIDStrength; } @@ -618,35 +615,35 @@ namespace OpenSim.Region.Framework.Scenes set { m_LoopSoundSlavePrims = value; } } - [XmlIgnore] + public Byte[] TextureAnimation { get { return m_TextureAnimation; } set { m_TextureAnimation = value; } } - [XmlIgnore] + public Byte[] ParticleSystem { get { return m_particleSystem; } set { m_particleSystem = value; } } - [XmlIgnore] + public DateTime Expires { get { return m_expires; } set { m_expires = value; } } - [XmlIgnore] + public DateTime Rezzed { get { return m_rezzed; } set { m_rezzed = value; } } - [XmlIgnore] + public float Damage { get { return m_damage; } @@ -1019,7 +1016,7 @@ namespace OpenSim.Region.Framework.Scenes } } - [XmlIgnore] + public bool CreateSelected { get { return m_createSelected; } @@ -1201,14 +1198,14 @@ namespace OpenSim.Region.Framework.Scenes } } - [XmlIgnore] + public UUID SitTargetAvatar { get { return m_sitTargetAvatar; } set { m_sitTargetAvatar = value; } } - [XmlIgnore] + public virtual UUID RegionID { get @@ -1222,7 +1219,7 @@ namespace OpenSim.Region.Framework.Scenes } private UUID _parentUUID = UUID.Zero; - [XmlIgnore] + public UUID ParentUUID { get @@ -1236,7 +1233,7 @@ namespace OpenSim.Region.Framework.Scenes set { _parentUUID = value; } } - [XmlIgnore] + public string SitAnimation { get { return m_sitAnimation; } @@ -1850,7 +1847,7 @@ namespace OpenSim.Region.Framework.Scenes /// /// /// - public static SceneObjectPart FromXml(XmlReader xmlReader) + public static SceneObjectPart FromXml(XmlTextReader xmlReader) { return FromXml(UUID.Zero, xmlReader); } @@ -1861,9 +1858,9 @@ namespace OpenSim.Region.Framework.Scenes /// The inventory id from which this part came, if applicable /// /// - public static SceneObjectPart FromXml(UUID fromUserInventoryItemId, XmlReader xmlReader) + public static SceneObjectPart FromXml(UUID fromUserInventoryItemId, XmlTextReader xmlReader) { - SceneObjectPart part = (SceneObjectPart)serializer.Deserialize(xmlReader); + SceneObjectPart part = SceneObjectSerializer.Xml2ToSOP(xmlReader); part.m_fromUserInventoryItemID = fromUserInventoryItemId; // for tempOnRez objects, we have to fix the Expire date. @@ -4058,9 +4055,9 @@ namespace OpenSim.Region.Framework.Scenes /// Serialize this part to xml. /// /// - public void ToXml(XmlWriter xmlWriter) + public void ToXml(XmlTextWriter xmlWriter) { - serializer.Serialize(xmlWriter, this); + SceneObjectSerializer.SOPToXml2(xmlWriter, this, new Dictionary()); } public void TriggerScriptChangedEvent(Changed val) diff --git a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs index 9a00beaff5..252304b876 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs @@ -67,14 +67,6 @@ namespace OpenSim.Region.Framework.Scenes.Serialization //m_log.DebugFormat("[SOG]: Starting deserialization of SOG"); //int time = System.Environment.TickCount; - // libomv.types changes UUID to Guid - xmlData = xmlData.Replace("", ""); - xmlData = xmlData.Replace("", ""); - - // Handle Nested property - xmlData = xmlData.Replace("", ""); - xmlData = xmlData.Replace("", ""); - try { StringReader sr; @@ -126,6 +118,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization } } + /// /// Serialize a scene object to the original xml format /// @@ -156,7 +149,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty); writer.WriteStartElement(String.Empty, "RootPart", String.Empty); - ToOriginalXmlFormat(sceneObject.RootPart, writer); + ToXmlFormat(sceneObject.RootPart, writer); writer.WriteEndElement(); writer.WriteStartElement(String.Empty, "OtherParts", String.Empty); @@ -167,7 +160,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization if (part.UUID != sceneObject.RootPart.UUID) { writer.WriteStartElement(String.Empty, "Part", String.Empty); - ToOriginalXmlFormat(part, writer); + ToXmlFormat(part, writer); writer.WriteEndElement(); } } @@ -179,9 +172,9 @@ namespace OpenSim.Region.Framework.Scenes.Serialization //m_log.DebugFormat("[SERIALIZER]: Finished serialization of SOG {0}, {1}ms", Name, System.Environment.TickCount - time); } - protected static void ToOriginalXmlFormat(SceneObjectPart part, XmlTextWriter writer) + protected static void ToXmlFormat(SceneObjectPart part, XmlTextWriter writer) { - part.ToXml(writer); + SOPToXml2(writer, part, new Dictionary()); } public static SceneObjectGroup FromXml2Format(string xmlData) @@ -189,14 +182,6 @@ namespace OpenSim.Region.Framework.Scenes.Serialization //m_log.DebugFormat("[SOG]: Starting deserialization of SOG"); //int time = System.Environment.TickCount; - // libomv.types changes UUID to Guid - xmlData = xmlData.Replace("", ""); - xmlData = xmlData.Replace("", ""); - - // Handle Nested property - xmlData = xmlData.Replace("", ""); - xmlData = xmlData.Replace("", ""); - try { XmlDocument doc = new XmlDocument(); @@ -261,41 +246,13 @@ namespace OpenSim.Region.Framework.Scenes.Serialization { using (XmlTextWriter writer = new XmlTextWriter(sw)) { - ToXml2Format(sceneObject, writer); + SOGToXml2(writer, sceneObject, new Dictionary()); } return sw.ToString(); } } - /// - /// Serialize a scene object to the 'xml2' format. - /// - /// - /// - public static void ToXml2Format(SceneObjectGroup sceneObject, XmlTextWriter writer) - { - //m_log.DebugFormat("[SERIALIZER]: Starting serialization of SOG {0} to XML2", Name); - //int time = System.Environment.TickCount; - - writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty); - sceneObject.RootPart.ToXml(writer); - writer.WriteStartElement(String.Empty, "OtherParts", String.Empty); - - SceneObjectPart[] parts = sceneObject.Parts; - for (int i = 0; i < parts.Length; i++) - { - SceneObjectPart part = parts[i]; - if (part.UUID != sceneObject.RootPart.UUID) - part.ToXml(writer); - } - - writer.WriteEndElement(); // End of OtherParts - sceneObject.SaveScriptedState(writer); - writer.WriteEndElement(); // End of SceneObjectGroup - - //m_log.DebugFormat("[SERIALIZER]: Finished serialization of SOG {0} to XML2, {1}ms", Name, System.Environment.TickCount - time); - } #region manual serialization @@ -386,6 +343,8 @@ namespace OpenSim.Region.Framework.Scenes.Serialization m_TaskInventoryXmlProcessors.Add("PermsGranter", ProcessTIPermsGranter); m_TaskInventoryXmlProcessors.Add("PermsMask", ProcessTIPermsMask); m_TaskInventoryXmlProcessors.Add("Type", ProcessTIType); + m_TaskInventoryXmlProcessors.Add("OwnerChanged", ProcessTIOwnerChanged); + #endregion #region ShapeXmlProcessors initialization @@ -817,6 +776,11 @@ namespace OpenSim.Region.Framework.Scenes.Serialization item.Type = reader.ReadElementContentAsInt("Type", String.Empty); } + private static void ProcessTIOwnerChanged(TaskInventoryItem item, XmlTextReader reader) + { + item.OwnerChanged = reader.ReadElementContentAsBoolean("OwnerChanged", String.Empty); + } + #endregion #region ShapeXmlProcessors @@ -1078,20 +1042,20 @@ namespace OpenSim.Region.Framework.Scenes.Serialization public static void SOGToXml2(XmlTextWriter writer, SceneObjectGroup sog, Dictionaryoptions) { writer.WriteStartElement(String.Empty, "SceneObjectGroup", String.Empty); - SOPToXml2(writer, sog.RootPart, null, options); + SOPToXml2(writer, sog.RootPart, options); writer.WriteStartElement(String.Empty, "OtherParts", String.Empty); sog.ForEachPart(delegate(SceneObjectPart sop) { if (sop.UUID != sog.RootPart.UUID) - SOPToXml2(writer, sop, sog.RootPart, options); + SOPToXml2(writer, sop, options); }); writer.WriteEndElement(); writer.WriteEndElement(); } - static void SOPToXml2(XmlTextWriter writer, SceneObjectPart sop, SceneObjectPart parent, Dictionary options) + public static void SOPToXml2(XmlTextWriter writer, SceneObjectPart sop, Dictionary options) { writer.WriteStartElement("SceneObjectPart"); writer.WriteAttributeString("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); @@ -1229,6 +1193,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization WriteUUID(writer, "PermsGranter", item.PermsGranter, options); writer.WriteElementString("PermsMask", item.PermsMask.ToString()); writer.WriteElementString("Type", item.Type.ToString()); + writer.WriteElementString("OwnerChanged", item.OwnerChanged.ToString().ToLower()); writer.WriteEndElement(); // TaskInventoryItem } @@ -1398,7 +1363,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization } else { - //m_log.DebugFormat("[SceneObjectSerializer]: caught unknown element {0}", nodeName); + m_log.DebugFormat("[SceneObjectSerializer]: caught unknown element {0}", nodeName); reader.ReadOuterXml(); // ignore } diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs index 3e3a0f0589..72dce6ddcb 100644 --- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs +++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs @@ -40,6 +40,7 @@ using log4net; using Nini.Config; using System.Reflection; using System.IO; +using ComponentAce.Compression.Libs.zlib; namespace OpenSim.Region.Physics.Meshing { @@ -74,6 +75,7 @@ namespace OpenSim.Region.Physics.Meshing private bool cacheSculptMaps = true; private string decodedSculptMapPath = null; + private bool useMeshiesPhysicsMesh = false; private float minSizeForComplexMesh = 0.2f; // prims with all dimensions smaller than this will have a bounding box mesh @@ -85,6 +87,7 @@ namespace OpenSim.Region.Physics.Meshing decodedSculptMapPath = start_config.GetString("DecodedSculptMapPath","j2kDecodeCache"); cacheSculptMaps = start_config.GetBoolean("CacheSculptMaps", cacheSculptMaps); + useMeshiesPhysicsMesh = start_config.GetBoolean("UseMeshiesPhysicsMesh", useMeshiesPhysicsMesh); try { @@ -268,73 +271,113 @@ namespace OpenSim.Region.Physics.Meshing { if (((OpenMetaverse.SculptType)primShape.SculptType) == SculptType.Mesh) { - // add code for mesh physics proxy generation here - m_log.Debug("[MESH]: mesh proxy generation not implemented yet "); + if (!useMeshiesPhysicsMesh) + return null; + + m_log.Debug("[MESH]: experimental mesh proxy generation"); OSD meshOsd; - if (primShape.SculptData.Length > 0) - { - - - m_log.Debug("[MESH]: asset data length: " + primShape.SculptData.Length.ToString()); - byte[] header = Util.StringToBytes256(""); - - ////dump to debugging file - //string filename = System.IO.Path.Combine(decodedSculptMapPath, "mesh_" + primShape.SculptTexture.ToString()); - //BinaryWriter writer = new BinaryWriter(File.Open(filename, FileMode.Create)); - //writer.Write(primShape.SculptData); - //writer.Close(); - - } - else + if (primShape.SculptData.Length <= 0) { m_log.Error("[MESH]: asset data is zero length"); return null; } - try + long start = 0; + using (MemoryStream data = new MemoryStream(primShape.SculptData)) { - meshOsd = OSDParser.DeserializeLLSDBinary(primShape.SculptData, true); - } - catch (Exception e) - { - m_log.Error("[MESH]: exception decoding mesh asset: " + e.ToString()); - return null; + meshOsd = (OSDMap)OSDParser.DeserializeLLSDBinary(data, true); + start = data.Position; } if (meshOsd is OSDMap) { OSDMap map = (OSDMap)meshOsd; - //foreach (string name in map.Keys) - // m_log.Debug("[MESH]: key:" + name + " value:" + map[name].AsString()); OSDMap physicsParms = (OSDMap)map["physics_shape"]; - int physOffset = physicsParms["offset"].AsInteger(); + int physOffset = physicsParms["offset"].AsInteger() + (int)start; int physSize = physicsParms["size"].AsInteger(); if (physOffset < 0 || physSize == 0) return null; // no mesh data in asset - m_log.Debug("[MESH]: physOffset:" + physOffset.ToString() + " physSize:" + physSize.ToString()); - //MemoryStream ms = new MemoryStream(primShape.SculptData, physOffset, physSize); - //GZipStream gzStream = new GZipStream(ms, CompressionMode.Decompress); - - //int maxSize = physSize * 5; // arbitrary guess - //byte[] readBuffer = new byte[maxSize]; + OSD decodedMeshOsd = new OSD(); + byte[] meshBytes = new byte[physSize]; + System.Buffer.BlockCopy(primShape.SculptData, physOffset, meshBytes, 0, physSize); + byte[] decompressed = new byte[physSize * 5]; + try + { + using (MemoryStream inMs = new MemoryStream(meshBytes)) + { + using (MemoryStream outMs = new MemoryStream()) + { + using (ZOutputStream zOut = new ZOutputStream(outMs)) + { + byte[] readBuffer = new byte[2048]; + int readLen = 0; + while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0) + { + zOut.Write(readBuffer, 0, readLen); + } + zOut.Flush(); + outMs.Seek(0, SeekOrigin.Begin); - //int bytesRead = gzStream.Read(readBuffer, 0, maxSize); + byte[] decompressedBuf = outMs.GetBuffer(); - //OSD physMeshOsd = OSDParser.DeserializeLLSDBinary(readBuffer); + decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf, true); + } + } + } + } + catch (Exception e) + { + m_log.Error("[MESH]: exception decoding physical mesh: " + e.ToString()); + return null; + } + OSDArray decodedMeshOsdArray = null; + // physics_shape is an array of OSDMaps, one for each submesh + if (decodedMeshOsd is OSDArray) + { + decodedMeshOsdArray = (OSDArray)decodedMeshOsd; + foreach (OSD subMeshOsd in decodedMeshOsdArray) + { + if (subMeshOsd is OSDMap) + { + OSDMap subMeshMap = (OSDMap)subMeshOsd; + OpenMetaverse.Vector3 posMax = ((OSDMap)subMeshMap["PositionDomain"])["Max"].AsVector3(); + OpenMetaverse.Vector3 posMin = ((OSDMap)subMeshMap["PositionDomain"])["Min"].AsVector3(); + byte[] posBytes = subMeshMap["Position"].AsBinary(); + for (int i = 0; i < posBytes.Length; i += 6) + { + ushort uX = Utils.BytesToUInt16(posBytes, i); + ushort uY = Utils.BytesToUInt16(posBytes, i + 2); + ushort uZ = Utils.BytesToUInt16(posBytes, i + 4); + Coord c = new Coord( + Utils.UInt16ToFloat(uX, posMin.X, posMax.X) * size.X, + Utils.UInt16ToFloat(uY, posMin.Y, posMax.Y) * size.Y, + Utils.UInt16ToFloat(uZ, posMin.Z, posMax.Z) * size.Z); + + coords.Add(c); + } + + byte[] triangleBytes = subMeshMap["TriangleList"].AsBinary(); + for (int i = 0; i < triangleBytes.Length; i += 6) + { + ushort v1 = Utils.BytesToUInt16(triangleBytes, i); + ushort v2 = Utils.BytesToUInt16(triangleBytes, i + 2); + ushort v3 = Utils.BytesToUInt16(triangleBytes, i + 4); + Face f = new Face(v1, v2, v3); + faces.Add(f); + } + } + } + } } - - //just bail out for now until mesh code is finished - return null; - } else { diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini index 05358c4373..82267edcc9 100644 --- a/bin/OpenSimDefaults.ini +++ b/bin/OpenSimDefaults.ini @@ -147,6 +147,12 @@ ; to false if you have compatibility problems. ;CacheSculptMaps = true + ; if you use Meshmerizer and want collisions for meshies, setting this to true + ; will cause OpenSim to attempt to decode meshies assets, extract the physics + ; mesh, and use it for collisions. This is currently experimental code and enabling + ; it may cause unexpected physics problems. + ;UseMeshiesPhysicsMesh = false + ; Choose one of the physics engines below ; OpenDynamicsEngine is by some distance the most developed physics engine ; basicphysics effectively does not model physics at all, making all objects phantom diff --git a/bin/zlib.net.dll b/bin/zlib.net.dll new file mode 100644 index 0000000000..9d156547f3 Binary files /dev/null and b/bin/zlib.net.dll differ diff --git a/prebuild.xml b/prebuild.xml index cc0424a692..b28d22697f 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -587,7 +587,6 @@ ../../../../bin/ - @@ -597,6 +596,7 @@ +