From 8106fccdd27527ef4fe93b9241a55eeae141977e Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Tue, 10 Mar 2009 01:54:45 +0000 Subject: [PATCH] * Changed Primitive.TextureEntry.ToBytes() to GetBytes() to follow naming conventions * Added Primitive.TreeSpecies and Primitive.ScratchPad * Converted Primitive.SoundFlags to the new SoundFlags enum * Added a Utils.BytesToString() overload that accepts index and count parameters * Added Utils.FloatToUInt16() [Simian] * Lots of changes in Simian to use the new unified ISceneProvider.ObjectAddOrUpdate() function * Update flags are checked to determine the minimum sized packet that needs to be sent out for an update. ImprovedTerseObjectUpdate is working, and started work on ObjectUpdateCached (updates using this will currently not send) * Adding three new variables to SimulationObject to store attachment-related state git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2478 52acb1d6-8a22-11de-b505-999d5b087335 --- .../MeshmerizerTypes.cs | 2 +- OpenMetaverse.Tests/PrimObjectTests.cs | 4 +- OpenMetaverse/AppearanceManager.cs | 2 +- OpenMetaverse/ObjectManager.cs | 41 +- OpenMetaverse/Primitives/Primitive.cs | 41 +- OpenMetaverse/Primitives/TextureEntry.cs | 2 +- OpenMetaverse/Types/UtilsConversions.cs | 18 + Programs/SimExport/OarFile.cs | 2 +- Programs/Simian/Extensions/Movement.cs | 87 +- Programs/Simian/Extensions/ObjectManager.cs | 97 +- Programs/Simian/Extensions/Periscope.cs | 9 +- .../Simian/Extensions/PeriscopeMovement.cs | 9 +- Programs/Simian/Extensions/PhysicsSimple.cs | 29 - Programs/Simian/Extensions/SceneManager.cs | 1500 +++++++++-------- Programs/Simian/Extensions/ScriptApi.cs | 255 +-- Programs/Simian/Interfaces/ISceneProvider.cs | 51 +- Programs/Simian/SimulationObject.cs | 122 +- 17 files changed, 1261 insertions(+), 1010 deletions(-) diff --git a/OpenMetaverse.Rendering.Meshmerizer/MeshmerizerTypes.cs b/OpenMetaverse.Rendering.Meshmerizer/MeshmerizerTypes.cs index 9b21963c..e008af8d 100644 --- a/OpenMetaverse.Rendering.Meshmerizer/MeshmerizerTypes.cs +++ b/OpenMetaverse.Rendering.Meshmerizer/MeshmerizerTypes.cs @@ -1119,7 +1119,7 @@ namespace OpenMetaverse.Rendering return new Primitive.TextureEntry(m_textureEntry, 0, m_textureEntry.Length); } - set { m_textureEntry = value.ToBytes(); } + set { m_textureEntry = value.GetBytes(); } } public byte[] TextureEntry diff --git a/OpenMetaverse.Tests/PrimObjectTests.cs b/OpenMetaverse.Tests/PrimObjectTests.cs index b3611d6c..54d93a24 100644 --- a/OpenMetaverse.Tests/PrimObjectTests.cs +++ b/OpenMetaverse.Tests/PrimObjectTests.cs @@ -147,11 +147,11 @@ namespace OpenMetaverse.Tests face.TexMapType = MappingType.Planar; face.TextureID = UUID.Random(); - byte[] teBytes = te.ToBytes(); + byte[] teBytes = te.GetBytes(); Primitive.TextureEntry te2 = new Primitive.TextureEntry(teBytes, 0, teBytes.Length); - byte[] teBytes2 = te2.ToBytes(); + byte[] teBytes2 = te2.GetBytes(); Assert.IsTrue(teBytes.Length == teBytes2.Length); diff --git a/OpenMetaverse/AppearanceManager.cs b/OpenMetaverse/AppearanceManager.cs index e3c8e03a..c0d11ca9 100644 --- a/OpenMetaverse/AppearanceManager.cs +++ b/OpenMetaverse/AppearanceManager.cs @@ -970,7 +970,7 @@ namespace OpenMetaverse } // Set the packet TextureEntry - set.ObjectData.TextureEntry = te.ToBytes(); + set.ObjectData.TextureEntry = te.GetBytes(); } // FIXME: Our hackish algorithm is making squished avatars. See diff --git a/OpenMetaverse/ObjectManager.cs b/OpenMetaverse/ObjectManager.cs index 8c1d6655..40f280b1 100644 --- a/OpenMetaverse/ObjectManager.cs +++ b/OpenMetaverse/ObjectManager.cs @@ -755,7 +755,7 @@ namespace OpenMetaverse image.ObjectData = new ObjectImagePacket.ObjectDataBlock[1]; image.ObjectData[0] = new ObjectImagePacket.ObjectDataBlock(); image.ObjectData[0].ObjectLocalID = localID; - image.ObjectData[0].TextureEntry = textures.ToBytes(); + image.ObjectData[0].TextureEntry = textures.GetBytes(); image.ObjectData[0].MediaURL = Utils.StringToBytes(mediaUrl); Client.Network.SendPacket(image, simulator); @@ -1537,7 +1537,7 @@ namespace OpenMetaverse // Sound information prim.Sound = block.Sound; - prim.SoundFlags = block.Flags; + prim.SoundFlags = (SoundFlags)block.Flags; prim.SoundGain = block.Gain; prim.SoundRadius = block.Radius; @@ -1558,8 +1558,24 @@ namespace OpenMetaverse prim.SetExtraParamsFromBytes(block.ExtraParams, 0); // PCode-specific data - prim.GenericData = block.Data; - + switch (pcode) + { + case PCode.Grass: + case PCode.Tree: + case PCode.NewTree: + if (block.Data.Length == 1) + prim.TreeSpecies = (Tree)block.Data[0]; + else + Logger.Log("Got a foliage update with an invalid TreeSpecies field", Helpers.LogLevel.Warning); + prim.ScratchPad = Utils.EmptyBytes; + break; + default: + prim.ScratchPad = new byte[block.Data.Length]; + if (block.Data.Length > 0) + Buffer.BlockCopy(block.Data, 0, prim.ScratchPad, 0, prim.ScratchPad.Length); + break; + } + // Packed parameters prim.CollisionPlane = collisionPlane; prim.Position = position; @@ -1614,7 +1630,7 @@ namespace OpenMetaverse avatar.AngularVelocity = angularVelocity; avatar.NameValues = nameValues; avatar.PrimData = data; - avatar.GenericData = block.Data; + if (block.Data.Length > 0) Logger.Log("Unexpected Data field for an avatar update, length " + block.Data.Length, Helpers.LogLevel.Warning); avatar.ParentID = block.ParentID; avatar.RegionHandle = update.RegionData.RegionHandle; @@ -1747,7 +1763,7 @@ namespace OpenMetaverse Utils.UInt16ToFloat(block.Data, pos + 4, -1.0f, 1.0f), Utils.UInt16ToFloat(block.Data, pos + 6, -1.0f, 1.0f)); pos += 8; - // Angular velocity + // Angular velocity (omega) update.AngularVelocity = new Vector3( Utils.UInt16ToFloat(block.Data, pos, -64.0f, 64.0f), Utils.UInt16ToFloat(block.Data, pos + 2, -64.0f, 64.0f), @@ -1892,15 +1908,17 @@ namespace OpenMetaverse // Tree data if ((flags & CompressedFlags.Tree) != 0) { - prim.GenericData = new byte[1]; - prim.GenericData[0] = block.Data[i++]; + prim.TreeSpecies = (Tree)block.Data[i++]; + prim.ScratchPad = Utils.EmptyBytes; } // Scratch pad else if ((flags & CompressedFlags.ScratchPad) != 0) { + prim.TreeSpecies = (Tree)0; + int size = block.Data[i++]; - prim.GenericData = new byte[size]; - Buffer.BlockCopy(block.Data, i, prim.GenericData, 0, size); + prim.ScratchPad = new byte[size]; + Buffer.BlockCopy(block.Data, i, prim.ScratchPad, 0, size); i += size; } @@ -1920,7 +1938,6 @@ namespace OpenMetaverse // Text color prim.TextColor = new Color4(block.Data, i, false); - // FIXME: Is alpha inversed here as well? i += 4; } else @@ -1960,7 +1977,7 @@ namespace OpenMetaverse prim.SoundGain = Utils.BytesToFloat(block.Data, i); i += 4; - prim.SoundFlags = block.Data[i++]; + prim.SoundFlags = (SoundFlags)block.Data[i++]; prim.SoundRadius = Utils.BytesToFloat(block.Data, i); i += 4; } diff --git a/OpenMetaverse/Primitives/Primitive.cs b/OpenMetaverse/Primitives/Primitive.cs index 562108b6..e8e26f30 100644 --- a/OpenMetaverse/Primitives/Primitive.cs +++ b/OpenMetaverse/Primitives/Primitive.cs @@ -139,6 +139,28 @@ namespace OpenMetaverse ZlibCompressed = 0x80000000 } + /// + /// Sound flags for sounds attached to primitives + /// + [Flags] + public enum SoundFlags : byte + { + /// + None = 0, + /// + Loop = 0x01, + /// + SyncMaster = 0x02, + /// + SyncSlave = 0x04, + /// + SyncPending = 0x08, + /// + Queue = 0x10, + /// + Stop = 0x20 + } + public enum ProfileCurve : byte { Circle = 0x00, @@ -1004,8 +1026,11 @@ namespace OpenMetaverse public ulong RegionHandle; /// public PrimFlags Flags; + /// Foliage type for this primitive. Only applicable if this + /// primitive is foliage + public Tree TreeSpecies; /// Unknown - public byte[] GenericData; + public byte[] ScratchPad; /// public Vector3 Position; /// @@ -1034,7 +1059,7 @@ namespace OpenMetaverse /// active public UUID OwnerID; /// - public byte SoundFlags; + public SoundFlags SoundFlags; /// public float SoundGain; /// @@ -1150,14 +1175,14 @@ namespace OpenMetaverse ParentID = prim.ParentID; RegionHandle = prim.RegionHandle; Flags = prim.Flags; - if (prim.GenericData != null) + TreeSpecies = prim.TreeSpecies; + if (prim.ScratchPad != null) { - if (GenericData == null || GenericData.Length != prim.GenericData.Length) - GenericData = new byte[prim.GenericData.Length]; - Buffer.BlockCopy(prim.GenericData, 0, GenericData, 0, prim.GenericData.Length); + ScratchPad = new byte[prim.ScratchPad.Length]; + Buffer.BlockCopy(prim.ScratchPad, 0, ScratchPad, 0, ScratchPad.Length); } else - GenericData = null; + ScratchPad = null; Position = prim.Position; Scale = prim.Scale; Rotation = prim.Rotation; @@ -1193,7 +1218,7 @@ namespace OpenMetaverse // FIXME: Get a real copy constructor for TextureEntry instead of serializing to bytes and back if (prim.Textures != null) { - byte[] textureBytes = prim.Textures.ToBytes(); + byte[] textureBytes = prim.Textures.GetBytes(); Textures = new TextureEntry(textureBytes, 0, textureBytes.Length); } else diff --git a/OpenMetaverse/Primitives/TextureEntry.cs b/OpenMetaverse/Primitives/TextureEntry.cs index 7a1cbac2..47a38b1b 100644 --- a/OpenMetaverse/Primitives/TextureEntry.cs +++ b/OpenMetaverse/Primitives/TextureEntry.cs @@ -842,7 +842,7 @@ namespace OpenMetaverse /// /// /// - public byte[] ToBytes() + public byte[] GetBytes() { if (DefaultTexture == null) return Utils.EmptyBytes; diff --git a/OpenMetaverse/Types/UtilsConversions.cs b/OpenMetaverse/Types/UtilsConversions.cs index 7ed83806..81f64773 100644 --- a/OpenMetaverse/Types/UtilsConversions.cs +++ b/OpenMetaverse/Types/UtilsConversions.cs @@ -409,6 +409,14 @@ namespace OpenMetaverse return UTF8Encoding.UTF8.GetString(bytes, 0, bytes.Length); } + public static string BytesToString(byte[] bytes, int index, int count) + { + if (bytes.Length > index + count && bytes[index + count - 1] == 0x00) + return UTF8Encoding.UTF8.GetString(bytes, index, count - 1); + else + return UTF8Encoding.UTF8.GetString(bytes, index, count); + } + /// /// Converts a byte array to a string containing hexadecimal characters /// @@ -638,6 +646,16 @@ namespace OpenMetaverse return fval; } + public static ushort FloatToUInt16(float value, float lower, float upper) + { + float delta = upper - lower; + value -= lower; + value /= delta; + value *= (float)UInt16.MaxValue; + + return (ushort)value; + } + #endregion Packed Values #region TryParse diff --git a/Programs/SimExport/OarFile.cs b/Programs/SimExport/OarFile.cs index aafe4d1f..967b3fd3 100644 --- a/Programs/SimExport/OarFile.cs +++ b/Programs/SimExport/OarFile.cs @@ -202,7 +202,7 @@ namespace SimExport byte[] te; if (prim.Textures != null) - te = prim.Textures.ToBytes(); + te = prim.Textures.GetBytes(); else te = Utils.EmptyBytes; diff --git a/Programs/Simian/Extensions/Movement.cs b/Programs/Simian/Extensions/Movement.cs index 0cad8853..e998a6f4 100644 --- a/Programs/Simian/Extensions/Movement.cs +++ b/Programs/Simian/Extensions/Movement.cs @@ -4,6 +4,7 @@ using System.Threading; using ExtensionLoader; using OpenMetaverse; using OpenMetaverse.Packets; +using OpenMetaverse.Rendering; namespace Simian.Extensions { @@ -46,6 +47,8 @@ namespace Simian.Extensions { this.server = server; + server.Scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate; + server.UDP.RegisterPacketCallback(PacketType.AgentRequestSit, AgentRequestSitHandler); server.UDP.RegisterPacketCallback(PacketType.AgentSit, AgentSitHandler); server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler); @@ -65,6 +68,27 @@ namespace Simian.Extensions } } + void Scene_OnObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags update) + { + bool forceMeshing = false; + bool forceTransform = false; + + if ((update & UpdateFlags.Scale) != 0 || + (update & UpdateFlags.Position) != 0 || + (update & UpdateFlags.Rotation) != 0) + { + forceTransform = true; + } + + if ((update & UpdateFlags.PrimData) != 0) + { + forceMeshing = true; + } + + // TODO: This doesn't update children prims when their parents move + obj.GetWorldMesh(DetailLevel.Low, forceMeshing, forceTransform); + } + void UpdateTimer_Elapsed(object sender) { int tick = Environment.TickCount; @@ -427,12 +451,13 @@ namespace Simian.Extensions { agent.Avatar.Prim.Flags &= ~PrimFlags.Physics; agent.Avatar.Prim.ParentID = obj.Prim.LocalID; - agent.Avatar.Prim.Position = Vector3.Zero; - agent.Avatar.Prim.Position.X = obj.Prim.Scale.X * 0.5f; - agent.Avatar.Prim.Position.Z = obj.Prim.Scale.Z * 0.5f; - agent.Avatar.Prim.Position.Z += agent.Avatar.Prim.Scale.Z * 0.33f; + agent.Avatar.Prim.Position = new Vector3( + obj.Prim.Scale.X * 0.5f, + obj.Prim.Scale.Z * 0.5f, + agent.Avatar.Prim.Scale.Z * 0.33f); - server.Scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None, + UpdateFlags.PrimFlags | UpdateFlags.ParentID | UpdateFlags.Position); server.Avatars.SetDefaultAnimation(agent, Animations.SIT); server.Avatars.SendAnimations(agent); } @@ -450,40 +475,36 @@ namespace Simian.Extensions { AgentUpdatePacket update = (AgentUpdatePacket)packet; - if (agent.Avatar.Prim.ParentID == 0) - agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation; - - agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags; - agent.State = (AgentState)update.AgentData.State; - agent.HideTitle = update.AgentData.Flags != 0; - - if (agent.Avatar.Prim.ParentID > 0 && (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) == AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) + SimulationObject obj; + if (server.Scene.TryGetObject(agent.ID, out obj)) { - SimulationObject obj; - if (server.Scene.TryGetObject(agent.Avatar.Prim.ParentID, out obj)) + if (agent.Avatar.Prim.ParentID == 0) + agent.Avatar.Prim.Rotation = update.AgentData.BodyRotation; + + agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags; + agent.State = (AgentState)update.AgentData.State; + agent.HideTitle = update.AgentData.Flags != 0; + + // Check for standing up + SimulationObject parent; + if (server.Scene.TryGetObject(agent.Avatar.Prim.ParentID, out parent) && + agent.Avatar.Prim.ParentID > 0 && + (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) == AgentManager.ControlFlags.AGENT_CONTROL_STAND_UP) { - agent.Avatar.Prim.Position = obj.Prim.Position - + Vector3.Transform(obj.SitPosition, Matrix4.CreateFromQuaternion(obj.SitRotation)) + agent.Avatar.Prim.Position = parent.Prim.Position + + Vector3.Transform(parent.SitPosition, Matrix4.CreateFromQuaternion(parent.SitRotation)) + Vector3.UnitZ; - } - else - { - //TODO: get position from course locations? - agent.Avatar.Prim.Position = Vector3.Zero; + + agent.Avatar.Prim.ParentID = 0; + + server.Avatars.SetDefaultAnimation(agent, Animations.STAND); + server.Avatars.SendAnimations(agent); + + agent.Avatar.Prim.Flags |= PrimFlags.Physics; } - agent.Avatar.Prim.ParentID = 0; - - server.Avatars.SetDefaultAnimation(agent, Animations.STAND); - server.Avatars.SendAnimations(agent); - - agent.Avatar.Prim.Flags |= PrimFlags.Physics; + server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position | UpdateFlags.Rotation); } - - ObjectUpdatePacket fullUpdate = SimulationObject.BuildFullUpdate(agent.Avatar.Prim, - server.Scene.RegionHandle, agent.Avatar.Prim.Flags, 0); - - server.UDP.BroadcastPacket(fullUpdate, PacketCategory.State); } void SetAlwaysRunHandler(Packet packet, Agent agent) diff --git a/Programs/Simian/Extensions/ObjectManager.cs b/Programs/Simian/Extensions/ObjectManager.cs index 1569f17d..98380594 100644 --- a/Programs/Simian/Extensions/ObjectManager.cs +++ b/Programs/Simian/Extensions/ObjectManager.cs @@ -164,7 +164,7 @@ namespace Simian.Extensions // Add this prim to the object database SimulationObject simObj = new SimulationObject(prim, server); - server.Scene.ObjectAddOrUpdate(this, simObj, agent.ID, 0, flags); + server.Scene.ObjectAddOrUpdate(this, simObj, agent.ID, 0, flags, UpdateFlags.FullUpdate); } void ObjectAttachHandler(Packet packet, Agent agent) @@ -174,18 +174,21 @@ namespace Simian.Extensions for (int i = 0; i < attach.ObjectData.Length; i++) { SimulationObject obj; - if (!server.Scene.TryGetObject(attach.ObjectData[i].ObjectLocalID, out obj)) - continue; + if (server.Scene.TryGetObject(attach.ObjectData[i].ObjectLocalID, out obj)) + { + obj.BeforeAttachmentRotation = attach.ObjectData[i].Rotation; - obj.Prim.ParentID = agent.Avatar.Prim.LocalID; - obj.Prim.Position = Vector3.Zero; //TODO: simulationObject.AttachmentPoint - obj.Prim.Rotation = attach.ObjectData[i].Rotation; //TODO: simulationObject.AttachmentRot ? + obj.Prim.ParentID = agent.Avatar.Prim.LocalID; + obj.Prim.Position = obj.AttachmentPosition; + obj.Prim.Rotation = obj.AttachmentRotation; - AttachmentPoint point = (AttachmentPoint)attach.AgentData.AttachmentPoint; - obj.Prim.PrimData.AttachmentPoint = point == AttachmentPoint.Default ? obj.LastAttachmentPoint : point; + AttachmentPoint point = (AttachmentPoint)attach.AgentData.AttachmentPoint; + obj.Prim.PrimData.AttachmentPoint = (point == AttachmentPoint.Default ? obj.LastAttachmentPoint : point); - // Send an update out to everyone - server.Scene.ObjectAddOrUpdate(this, obj, agent.ID, 0, obj.Prim.Flags); + // Send an update out to everyone + server.Scene.ObjectAddOrUpdate(this, obj, agent.ID, 0, obj.Prim.Flags, + UpdateFlags.ParentID | UpdateFlags.Position | UpdateFlags.Rotation | UpdateFlags.AttachmentPoint); + } } } @@ -209,7 +212,7 @@ namespace Simian.Extensions newObj.Prim.LocalID = 0; newObj.Prim.Properties.CreationDate = DateTime.Now; - server.Scene.ObjectAddOrUpdate(this, newObj, agent.ID, 0, flags); + server.Scene.ObjectAddOrUpdate(this, newObj, agent.ID, 0, flags, UpdateFlags.FullUpdate); } else { @@ -310,12 +313,12 @@ namespace Simian.Extensions SimulationObject obj; if (!server.Scene.TryGetObject(link.ObjectData[i].ObjectLocalID, out obj)) { - //TODO: send an error message + //TODO: Send an error message return; } else if (obj.Prim.OwnerID != agent.ID) { - //TODO: send an error message + //TODO: Do a full permissions check return; } else @@ -330,32 +333,35 @@ namespace Simian.Extensions if (linkSet[i].Prim.ParentID > 0) { - //previously linked children + // Previously linked children SimulationObject parent; if (server.Scene.TryGetObject(linkSet[i].Prim.ParentID, out parent)) { - //re-add old root orientation - linkSet[i].Prim.Position = parent.Prim.Position + Vector3.Transform(linkSet[i].Prim.Position, Matrix4.CreateFromQuaternion(parent.Prim.Rotation)); + // Re-add old root orientation + linkSet[i].Prim.Position = parent.Prim.Position + Vector3.Transform(linkSet[i].Prim.Position, + Matrix4.CreateFromQuaternion(parent.Prim.Rotation)); linkSet[i].Prim.Rotation *= parent.Prim.Rotation; } } if (i > 0) { - //subtract root prim orientation - linkSet[i].Prim.Position = Vector3.Transform(linkSet[i].Prim.Position - linkSet[0].Prim.Position, Matrix4.CreateFromQuaternion(Quaternion.Identity / linkSet[0].Prim.Rotation)); + // Subtract root prim orientation + linkSet[i].Prim.Position = Vector3.Transform(linkSet[i].Prim.Position - linkSet[0].Prim.Position, + Matrix4.CreateFromQuaternion(Quaternion.Identity / linkSet[0].Prim.Rotation)); linkSet[i].Prim.Rotation /= linkSet[0].Prim.Rotation; - //set parent ID + // Set parent ID linkSet[i].Prim.ParentID = linkSet[0].Prim.LocalID; } else { - //root prim + // Root prim linkSet[i].Prim.ParentID = 0; } - server.Scene.ObjectAddOrUpdate(this, linkSet[i], agent.ID, 0, linkSet[i].Prim.Flags); + server.Scene.ObjectAddOrUpdate(this, linkSet[i], agent.ID, 0, linkSet[i].Prim.Flags, + UpdateFlags.Position | UpdateFlags.Rotation | UpdateFlags.ParentID); } } @@ -369,12 +375,12 @@ namespace Simian.Extensions SimulationObject obj; if (!server.Scene.TryGetObject(delink.ObjectData[i].ObjectLocalID, out obj)) { - //TODO: send an error message + //TODO: Send an error message return; } else if (obj.Prim.OwnerID != agent.ID) { - //TODO: send an error message + //TODO: Do a full permissions check return; } else @@ -388,14 +394,16 @@ namespace Simian.Extensions linkSet[i].Prim.ParentID = 0; linkSet[i].LinkNumber = 0; - //add root prim orientation to child prims + // Add root prim orientation to child prims if (i > 0) { - linkSet[i].Prim.Position = linkSet[0].Prim.Position + Vector3.Transform(linkSet[i].Prim.Position, Matrix4.CreateFromQuaternion(linkSet[0].Prim.Rotation)); + linkSet[i].Prim.Position = linkSet[0].Prim.Position + Vector3.Transform(linkSet[i].Prim.Position, + Matrix4.CreateFromQuaternion(linkSet[0].Prim.Rotation)); linkSet[i].Prim.Rotation *= linkSet[0].Prim.Rotation; } - server.Scene.ObjectAddOrUpdate(this, linkSet[i], agent.ID, 0, linkSet[i].Prim.Flags); + server.Scene.ObjectAddOrUpdate(this, linkSet[i], agent.ID, 0, linkSet[i].Prim.Flags, + UpdateFlags.Position | UpdateFlags.Rotation | UpdateFlags.ParentID); } } @@ -431,7 +439,8 @@ namespace Simian.Extensions data.ProfileEnd = Primitive.UnpackEndCut(block.ProfileEnd); data.ProfileHollow = Primitive.UnpackProfileHollow(block.ProfileHollow); - server.Scene.ObjectModify(this, obj, data); + obj.Prim.PrimData = data; + server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimData); } else { @@ -470,7 +479,8 @@ namespace Simian.Extensions else flags &= ~PrimFlags.Physics; - server.Scene.ObjectFlags(this, obj, flags); + obj.Prim.Flags = flags; + server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimFlags); } else { @@ -523,7 +533,7 @@ namespace Simian.Extensions } } - server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.ExtraData); } } } @@ -536,9 +546,11 @@ namespace Simian.Extensions { SimulationObject obj; if (server.Scene.TryGetObject(image.ObjectData[i].ObjectLocalID, out obj)) - server.Scene.ObjectModifyTextures(this, obj, - Utils.BytesToString(image.ObjectData[i].MediaURL), - new Primitive.TextureEntry(image.ObjectData[i].TextureEntry, 0, image.ObjectData[i].TextureEntry.Length)); + { + obj.Prim.MediaURL = Utils.BytesToString(image.ObjectData[i].MediaURL); + obj.Prim.Textures = new Primitive.TextureEntry(image.ObjectData[i].TextureEntry, 0, image.ObjectData[i].TextureEntry.Length); + server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.MediaURL | UpdateFlags.Textures); + } } } @@ -665,18 +677,23 @@ namespace Simian.Extensions Quaternion rotation = obj.Prim.Rotation; Vector3 scale = obj.Prim.Scale; + UpdateFlags updateFlags = UpdateFlags.None; + if ((type & UpdateType.Position) != 0) { + updateFlags |= UpdateFlags.Position; position = new Vector3(block.Data, pos); pos += 12; } if ((type & UpdateType.Rotation) != 0) { + updateFlags |= UpdateFlags.Rotation; rotation = new Quaternion(block.Data, pos, true); pos += 12; } if ((type & UpdateType.Scale) != 0) { + updateFlags |= UpdateFlags.Scale; scaled = true; scale = new Vector3(block.Data, pos); pos += 12; @@ -685,19 +702,11 @@ namespace Simian.Extensions //bool uniform = ((type & UpdateType.Uniform) != 0); } - if (scaled) - { - obj.Prim.Position = position; - obj.Prim.Rotation = rotation; - obj.Prim.Scale = scale; + obj.Prim.Position = position; + obj.Prim.Rotation = rotation; + if (scaled) obj.Prim.Scale = scale; - server.Scene.ObjectAddOrUpdate(this, obj, agent.ID, 0, PrimFlags.None); - } - else - { - server.Scene.ObjectTransform(this, obj, position, rotation, - obj.Prim.Velocity, obj.Prim.Acceleration, obj.Prim.AngularVelocity); - } + server.Scene.ObjectAddOrUpdate(this, obj, agent.ID, 0, PrimFlags.None, updateFlags); } else { diff --git a/Programs/Simian/Extensions/Periscope.cs b/Programs/Simian/Extensions/Periscope.cs index 4ee02acf..78a55ff8 100644 --- a/Programs/Simian/Extensions/Periscope.cs +++ b/Programs/Simian/Extensions/Periscope.cs @@ -72,13 +72,13 @@ namespace Simian.Extensions SimulationObject simObj = new SimulationObject(prim, server); if (MasterAgent != null) simObj.Prim.OwnerID = MasterAgent.ID; - server.Scene.ObjectAddOrUpdate(this, simObj, MasterAgent.ID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, simObj, MasterAgent.ID, 0, PrimFlags.None, UpdateFlags.FullUpdate); } void Objects_OnNewAttachment(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation) { SimulationObject simObj = new SimulationObject(prim, server); - server.Scene.ObjectAddOrUpdate(this, simObj, MasterAgent.ID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, simObj, MasterAgent.ID, 0, PrimFlags.None, UpdateFlags.FullUpdate); } void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation) @@ -101,8 +101,9 @@ namespace Simian.Extensions SimulationObject obj; if (server.Scene.TryGetObject(update.LocalID, out obj)) { - server.Scene.ObjectTransform(this, obj, update.Position, update.Rotation, update.Velocity, - update.Acceleration, update.AngularVelocity); + server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, + UpdateFlags.Acceleration | UpdateFlags.AngularVelocity | UpdateFlags.CollisionPlane | + UpdateFlags.Position | UpdateFlags.Rotation | UpdateFlags.Velocity); } if (update.LocalID == client.Self.LocalID) diff --git a/Programs/Simian/Extensions/PeriscopeMovement.cs b/Programs/Simian/Extensions/PeriscopeMovement.cs index ebca8f4d..6a3253b4 100644 --- a/Programs/Simian/Extensions/PeriscopeMovement.cs +++ b/Programs/Simian/Extensions/PeriscopeMovement.cs @@ -331,14 +331,9 @@ namespace Simian.Extensions SimulationObject obj; if (server.Scene.TryGetObject(update.AgentData.AgentID, out obj)) { - server.Scene.ObjectTransform(this, obj, obj.Prim.Position, update.AgentData.BodyRotation, obj.Prim.Velocity, - obj.Prim.Acceleration, obj.Prim.AngularVelocity); + obj.Prim.Rotation = update.AgentData.BodyRotation; + server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Rotation); } - - /*ObjectUpdatePacket fullUpdate = SimulationObject.BuildFullUpdate(agent.Avatar, - server.Scene.RegionHandle, agent.Flags); - - server.UDP.BroadcastPacket(fullUpdate, PacketCategory.State);*/ } void SetAlwaysRunHandler(Packet packet, Agent agent) diff --git a/Programs/Simian/Extensions/PhysicsSimple.cs b/Programs/Simian/Extensions/PhysicsSimple.cs index 2be7607b..d49489d9 100644 --- a/Programs/Simian/Extensions/PhysicsSimple.cs +++ b/Programs/Simian/Extensions/PhysicsSimple.cs @@ -16,10 +16,6 @@ namespace Simian.Extensions public void Start(Simian server) { this.server = server; - - server.Scene.OnObjectAdd += Scene_OnObjectAdd; - server.Scene.OnObjectModify += Scene_OnObjectModify; - server.Scene.OnObjectTransform += Scene_OnObjectTransform; } public void Stop() @@ -404,30 +400,5 @@ namespace Simian.Extensions return returnMass; } - - #region Callbacks - - void Scene_OnObjectAdd(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags) - { - // TODO: This doesn't update children prims when their parents move. "World meshes" are a bad approach in general, - // the transforms should probably be applied to the mesh in the collision test - obj.GetWorldMesh(DetailLevel.Low, true, true); - } - - void Scene_OnObjectModify(object sender, SimulationObject obj, Primitive.ConstructionData data) - { - obj.GetWorldMesh(DetailLevel.Low, true, false); - } - - void Scene_OnObjectTransform(object sender, SimulationObject obj, Vector3 position, Quaternion rotation, Vector3 velocity, - Vector3 acceleration, Vector3 angularVelocity) - { - // TODO: This doesn't update children prims when their parents move. "World meshes" are a bad approach in general, - // the transforms should probably be applied to the mesh in the collision test - if (position != obj.Prim.Position || rotation != obj.Prim.Rotation) - obj.GetWorldMesh(DetailLevel.Low, false, true); - } - - #endregion Callbacks } } diff --git a/Programs/Simian/Extensions/SceneManager.cs b/Programs/Simian/Extensions/SceneManager.cs index cb3cfdbf..ca73b88f 100644 --- a/Programs/Simian/Extensions/SceneManager.cs +++ b/Programs/Simian/Extensions/SceneManager.cs @@ -43,12 +43,8 @@ namespace Simian.Extensions TerrainPatch[,] heightmap = new TerrainPatch[16, 16]; Vector2[,] windSpeeds = new Vector2[16, 16]; - public event ObjectAddCallback OnObjectAdd; + public event ObjectAddOrUpdateCallback OnObjectAddOrUpdate; public event ObjectRemoveCallback OnObjectRemove; - public event ObjectTransformCallback OnObjectTransform; - public event ObjectFlagsCallback OnObjectFlags; - public event ObjectModifyCallback OnObjectModify; - public event ObjectModifyTexturesCallback OnObjectModifyTextures; public event ObjectSetRotationAxisCallback OnObjectSetRotationAxis; public event ObjectApplyImpulseCallback OnObjectApplyImpulse; public event ObjectApplyRotationalImpulseCallback OnObjectApplyRotationalImpulse; @@ -94,9 +90,749 @@ namespace Simian.Extensions public void Stop() { - ForEachAgent(delegate(Agent agent) { AgentRemove(this, agent); }); + while (sceneAgents.Count > 0) + { + Dictionary.ValueCollection.Enumerator e = sceneAgents.Values.GetEnumerator(); + e.MoveNext(); + AgentRemove(this, e.Current); + } } + public void ObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags updateFlags) + { + if (OnObjectAddOrUpdate != null) + { + OnObjectAddOrUpdate(sender, obj, ownerID, scriptStartParam, creatorFlags, updateFlags); + } + + #region Initialize new objects + + // Check if the object already exists in the scene + if (!sceneObjects.ContainsKey(obj.Prim.ID)) + { + // Enable some default flags that all objects will have + obj.Prim.Flags |= server.Permissions.GetDefaultObjectFlags(); + + // Object did not exist before, so there's no way it could contain inventory + obj.Prim.Flags |= PrimFlags.InventoryEmpty; + + // Fun Fact: Prim.OwnerID is only used by the LL viewer to mute sounds + obj.Prim.OwnerID = ownerID; + + // Other than storing tree species, I have no idea what this does + obj.Prim.ScratchPad = Utils.EmptyBytes; + + // Assign a unique LocalID to this object if no LocalID is set + if (obj.Prim.LocalID == 0) + obj.Prim.LocalID = (uint)Interlocked.Increment(ref currentLocalID); + + // Assign a random ID to this object if no ID is set + if (obj.Prim.ID == UUID.Zero) + obj.Prim.ID = UUID.Random(); + + // Set the RegionHandle if no RegionHandle is set + if (obj.Prim.RegionHandle == 0) + obj.Prim.RegionHandle = server.Scene.RegionHandle; + + // Make sure this object has properties + if (obj.Prim.Properties == null) + { + obj.Prim.Properties = new Primitive.ObjectProperties(); + obj.Prim.Properties.CreationDate = DateTime.Now; + obj.Prim.Properties.CreatorID = ownerID; + obj.Prim.Properties.Name = "New Object"; + obj.Prim.Properties.ObjectID = obj.Prim.ID; + obj.Prim.Properties.OwnerID = ownerID; + obj.Prim.Properties.Permissions = server.Permissions.GetDefaultPermissions(); + obj.Prim.Properties.SalePrice = 10; + } + + // Set the default scale + if (obj.Prim.Scale == Vector3.Zero) + obj.Prim.Scale = new Vector3(0.5f, 0.5f, 0.5f); + + // Set the collision plane + if (obj.Prim.CollisionPlane == Vector4.Zero) + obj.Prim.CollisionPlane = Vector4.UnitW; + + // Set default textures if none are set + if (obj.Prim.Textures == null) + obj.Prim.Textures = new Primitive.TextureEntry(new UUID("89556747-24cb-43ed-920b-47caed15465f")); // Plywood + + // Add the object to the scene dictionary + sceneObjects.Add(obj.Prim.LocalID, obj.Prim.ID, obj); + } + + #endregion Initialize new objects + + // Reset the prim CRC + obj.CRC = 0; + + #region UpdateFlags to packet type conversion + + bool canUseCompressed = true; + bool canUseImproved = true; + + if ((updateFlags & UpdateFlags.FullUpdate) == UpdateFlags.FullUpdate || creatorFlags != PrimFlags.None) + { + canUseCompressed = false; + canUseImproved = false; + } + else + { + if ((updateFlags & UpdateFlags.Velocity) != 0 || + (updateFlags & UpdateFlags.Acceleration) != 0 || + (updateFlags & UpdateFlags.CollisionPlane) != 0 || + (updateFlags & UpdateFlags.Joint) != 0) + { + canUseCompressed = false; + } + + if ((updateFlags & UpdateFlags.PrimFlags) != 0 || + (updateFlags & UpdateFlags.ParentID) != 0 || + (updateFlags & UpdateFlags.Scale) != 0 || + (updateFlags & UpdateFlags.PrimData) != 0 || + (updateFlags & UpdateFlags.Text) != 0 || + (updateFlags & UpdateFlags.NameValue) != 0 || + (updateFlags & UpdateFlags.ExtraData) != 0 || + (updateFlags & UpdateFlags.TextureAnim) != 0 || + (updateFlags & UpdateFlags.Sound) != 0 || + (updateFlags & UpdateFlags.Particles) != 0 || + (updateFlags & UpdateFlags.Material) != 0 || + (updateFlags & UpdateFlags.ClickAction) != 0 || + (updateFlags & UpdateFlags.MediaURL) != 0 || + (updateFlags & UpdateFlags.Joint) != 0) + { + canUseImproved = false; + } + } + + #endregion UpdateFlags to packet type conversion + + SendObjectPacket(obj, canUseCompressed, canUseImproved, creatorFlags, updateFlags); + } + + void SendObjectPacket(SimulationObject obj, bool canUseCompressed, bool canUseImproved, PrimFlags creatorFlags, UpdateFlags updateFlags) + { + if (!canUseImproved && !canUseCompressed) + { + #region ObjectUpdate + + Logger.DebugLog("Sending ObjectUpdate"); + + if (sceneAgents.ContainsKey(obj.Prim.OwnerID)) + { + // Send an update out to the creator + ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(obj.Prim, regionHandle, + obj.Prim.Flags | creatorFlags | PrimFlags.ObjectYouOwner, obj.CRC); + server.UDP.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State); + } + + // Send an update out to everyone else + ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(obj.Prim, regionHandle, + obj.Prim.Flags, obj.CRC); + server.Scene.ForEachAgent( + delegate(Agent recipient) + { + if (recipient.ID != obj.Prim.OwnerID) + server.UDP.SendPacket(recipient.ID, updateToOthers, PacketCategory.State); + } + ); + + #endregion ObjectUpdate + } + else if (!canUseImproved) + { + #region ObjectUpdateCompressed + + ObjectUpdateCompressedPacket update = new ObjectUpdateCompressedPacket(); + update.RegionData.RegionHandle = RegionHandle; + update.RegionData.TimeDilation = (ushort)(1f * (float)UInt16.MaxValue); // TODO: Implement this + update.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[1]; + update.ObjectData[0] = new ObjectUpdateCompressedPacket.ObjectDataBlock(); + + CompressedFlags flags = 0; + int size = 0; + byte[] textBytes; + byte[] mediaURLBytes; + + if ((updateFlags & UpdateFlags.AngularVelocity) != 0) + { + flags |= CompressedFlags.HasAngularVelocity; + size += 12; + } + if ((updateFlags & UpdateFlags.ParentID) != 0) + { + flags |= CompressedFlags.HasParent; + size += 4; + } + if ((updateFlags & UpdateFlags.ScratchPad) != 0) + { + switch (obj.Prim.PrimData.PCode) + { + case PCode.Grass: + case PCode.Tree: + case PCode.NewTree: + flags |= CompressedFlags.Tree; + size += 2; + break; + default: + flags |= CompressedFlags.ScratchPad; + size += 1 + obj.Prim.ScratchPad.Length; + break; + } + } + if ((updateFlags & UpdateFlags.Text) != 0) + { + flags |= CompressedFlags.HasText; + textBytes = Utils.StringToBytes(obj.Prim.Text); + size += textBytes.Length; + } + if ((updateFlags & UpdateFlags.MediaURL) != 0) + { + flags |= CompressedFlags.MediaURL; + mediaURLBytes = Utils.StringToBytes(obj.Prim.MediaURL); + size += mediaURLBytes.Length; + } + if ((updateFlags & UpdateFlags.Particles) != 0) + { + flags |= CompressedFlags.HasParticles; + // size += + } + // Extra Params + if ((updateFlags & UpdateFlags.Sound) != 0) + { + flags |= CompressedFlags.HasSound; + } + if ((updateFlags & UpdateFlags.NameValue) != 0) + { + flags |= CompressedFlags.HasNameValues; + //size += + } + // PrimData + // Texture Length + // Texture Entry + if ((updateFlags & UpdateFlags.TextureAnim) != 0) + { + flags |= CompressedFlags.TextureAnimation; + // size += 4 + + } + + Logger.DebugLog("Sending ObjectUpdateCompressed with " + flags.ToString()); + + update.ObjectData[0].UpdateFlags = (uint)flags; + //update.ObjectData[0].Data = data; + //server.UDP.BroadcastPacket(update, PacketCategory.State); + + #endregion ObjectUpdateCompressed + } + else + { + #region ImprovedTerseObjectUpdate + + Logger.DebugLog("Sending ImprovedTerseObjectUpdate"); + + int pos = 0; + byte[] data = new byte[(obj.Prim is Avatar ? 60 : 44)]; + + // LocalID + Utils.UIntToBytes(obj.Prim.LocalID, data, pos); + pos += 4; + // Avatar/CollisionPlane + data[pos++] = obj.Prim.PrimData.State; + if (obj.Prim is Avatar) + { + data[pos++] = 1; + obj.Prim.CollisionPlane.ToBytes(data, pos); + pos += 16; + } + else + { + ++pos; + } + // Position + obj.Prim.Position.ToBytes(data, pos); + pos += 12; + + // Velocity + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Velocity.X, -128.0f, 128.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Velocity.Y, -128.0f, 128.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Velocity.Z, -128.0f, 128.0f), data, pos); pos += 2; + // Acceleration + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Acceleration.X, -64.0f, 64.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Acceleration.Y, -64.0f, 64.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Acceleration.Z, -64.0f, 64.0f), data, pos); pos += 2; + // Rotation + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Rotation.X, -1.0f, 1.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Rotation.Y, -1.0f, 1.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Rotation.Z, -1.0f, 1.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.Rotation.W, -1.0f, 1.0f), data, pos); pos += 2; + // Angular Velocity + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.AngularVelocity.X, -64.0f, 64.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.AngularVelocity.Y, -64.0f, 64.0f), data, pos); pos += 2; + Utils.UInt16ToBytes(Utils.FloatToUInt16(obj.Prim.AngularVelocity.Z, -64.0f, 64.0f), data, pos); pos += 2; + + ImprovedTerseObjectUpdatePacket update = new ImprovedTerseObjectUpdatePacket(); + update.RegionData.RegionHandle = RegionHandle; + update.RegionData.TimeDilation = (ushort)(1f * (float)UInt16.MaxValue); // TODO: Implement this + update.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[1]; + update.ObjectData[0] = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock(); + update.ObjectData[0].Data = data; + + if ((updateFlags & UpdateFlags.Textures) != 0) + { + byte[] textureBytes = obj.Prim.Textures.GetBytes(); + byte[] textureEntry = new byte[textureBytes.Length + 4]; + + // Texture Length + Utils.IntToBytes(textureBytes.Length, textureEntry, 0); + // Texture + Buffer.BlockCopy(textureBytes, 0, textureEntry, 4, textureBytes.Length); + + update.ObjectData[0].TextureEntry = textureEntry; + } + else + { + update.ObjectData[0].TextureEntry = Utils.EmptyBytes; + } + + server.UDP.BroadcastPacket(update, PacketCategory.State); + + #endregion ImprovedTerseObjectUpdate + } + } + + public bool ObjectRemove(object sender, uint localID) + { + SimulationObject obj; + Agent agent; + + if (sceneObjects.TryGetValue(localID, out obj)) + { + if (sceneAgents.TryGetValue(obj.Prim.ID, out agent)) + AgentRemove(sender, agent); + + if (OnObjectRemove != null) + OnObjectRemove(sender, obj); + + sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID); + + KillObjectPacket kill = new KillObjectPacket(); + kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; + kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); + kill.ObjectData[0].ID = obj.Prim.LocalID; + + server.UDP.BroadcastPacket(kill, PacketCategory.State); + return true; + } + + return false; + } + + public bool ObjectRemove(object sender, UUID id) + { + SimulationObject obj; + Agent agent; + + if (sceneAgents.TryGetValue(id, out agent)) + AgentRemove(sender, agent); + + if (sceneObjects.TryGetValue(id, out obj)) + { + if (OnObjectRemove != null) + OnObjectRemove(sender, obj); + + sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID); + + KillObjectPacket kill = new KillObjectPacket(); + kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; + kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); + kill.ObjectData[0].ID = obj.Prim.LocalID; + + server.UDP.BroadcastPacket(kill, PacketCategory.State); + return true; + } + + return false; + } + + void AgentRemove(object sender, Agent agent) + { + if (OnAgentRemove != null) + OnAgentRemove(sender, agent); + + Logger.Log("Removing agent " + agent.FullName + " from the scene", Helpers.LogLevel.Info); + + lock (sceneAgents) sceneAgents.Remove(agent.ID); + + KillObjectPacket kill = new KillObjectPacket(); + kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; + kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); + kill.ObjectData[0].ID = agent.Avatar.Prim.LocalID; + + server.UDP.BroadcastPacket(kill, PacketCategory.State); + + // Kill the EventQueue + RemoveEventQueue(agent.ID); + + // Remove the UDP client + server.UDP.RemoveClient(agent); + + // Notify everyone in the scene that this agent has gone offline + OfflineNotificationPacket offline = new OfflineNotificationPacket(); + offline.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[1]; + offline.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock(); + offline.AgentBlock[0].AgentID = agent.ID; + server.UDP.BroadcastPacket(offline, PacketCategory.State); + } + + public void ObjectSetRotationAxis(object sender, SimulationObject obj, Vector3 rotationAxis) + { + if (OnObjectSetRotationAxis != null) + { + OnObjectSetRotationAxis(sender, obj, rotationAxis); + } + + // Update the object + obj.RotationAxis = rotationAxis; + } + + public void ObjectApplyImpulse(object sender, SimulationObject obj, Vector3 impulse) + { + if (OnObjectApplyImpulse != null) + { + OnObjectApplyImpulse(sender, obj, impulse); + } + + // FIXME: + } + + public void ObjectApplyRotationalImpulse(object sender, SimulationObject obj, Vector3 impulse) + { + if (OnObjectApplyRotationalImpulse != null) + { + OnObjectApplyRotationalImpulse(sender, obj, impulse); + } + + // FIXME: + } + + public void ObjectSetTorque(object sender, SimulationObject obj, Vector3 torque) + { + if (OnObjectSetTorque != null) + { + OnObjectSetTorque(sender, obj, torque); + } + + obj.Torque = torque; + } + + public void ObjectAnimate(object sender, UUID senderID, UUID objectID, AnimationTrigger[] animations) + { + if (OnObjectAnimate != null) + { + OnObjectAnimate(sender, senderID, objectID, animations); + } + + AvatarAnimationPacket sendAnim = new AvatarAnimationPacket(); + sendAnim.Sender.ID = senderID; + sendAnim.AnimationSourceList = new AvatarAnimationPacket.AnimationSourceListBlock[1]; + sendAnim.AnimationSourceList[0] = new AvatarAnimationPacket.AnimationSourceListBlock(); + sendAnim.AnimationSourceList[0].ObjectID = objectID; + + sendAnim.AnimationList = new AvatarAnimationPacket.AnimationListBlock[animations.Length]; + for (int i = 0; i < animations.Length; i++) + { + sendAnim.AnimationList[i] = new AvatarAnimationPacket.AnimationListBlock(); + sendAnim.AnimationList[i].AnimID = animations[i].AnimationID; + sendAnim.AnimationList[i].AnimSequenceID = animations[i].SequenceID; + } + + server.UDP.BroadcastPacket(sendAnim, PacketCategory.State); + } + + public void ObjectChat(object sender, UUID ownerID, UUID sourceID, ChatAudibleLevel audible, ChatType type, ChatSourceType sourceType, + string fromName, Vector3 position, int channel, string message) + { + if (OnObjectChat != null) + { + OnObjectChat(sender, ownerID, sourceID, audible, type, sourceType, fromName, position, channel, message); + } + + if (channel == 0) + { + // TODO: Reduction provider will impose the chat radius + ChatFromSimulatorPacket chat = new ChatFromSimulatorPacket(); + chat.ChatData.Audible = (byte)audible; + chat.ChatData.ChatType = (byte)type; + chat.ChatData.OwnerID = ownerID; + chat.ChatData.SourceID = sourceID; + chat.ChatData.SourceType = (byte)sourceType; + chat.ChatData.Position = position; + chat.ChatData.FromName = Utils.StringToBytes(fromName); + chat.ChatData.Message = Utils.StringToBytes(message); + + server.UDP.BroadcastPacket(chat, PacketCategory.Messaging); + } + } + + public void ObjectUndo(object sender, SimulationObject obj) + { + if (OnObjectUndo != null) + { + OnObjectUndo(sender, obj); + } + + Primitive prim = obj.UndoSteps.DequeueLast(); + if (prim != null) + { + Logger.Log(String.Format("Performing undo on object {0}", obj.Prim.ID), Helpers.LogLevel.Debug); + + obj.RedoSteps.Enqueue(prim); + obj.Prim = prim; + + // Inform clients + server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.FullUpdate); + } + else + { + Logger.Log(String.Format("Undo requested on object {0} with no remaining undo steps", obj.Prim.ID), + Helpers.LogLevel.Debug); + } + } + + public void ObjectRedo(object sender, SimulationObject obj) + { + if (OnObjectRedo != null) + { + OnObjectRedo(sender, obj); + } + + Primitive prim = obj.RedoSteps.DequeueLast(); + if (prim != null) + { + Logger.Log(String.Format("Performing redo on object {0}", obj.Prim.ID), Helpers.LogLevel.Debug); + + obj.UndoSteps.Enqueue(prim); + obj.Prim = prim; + + // Inform clients + server.Scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.FullUpdate); + } + else + { + Logger.Log(String.Format("Redo requested on object {0} with no remaining redo steps", obj.Prim.ID), + Helpers.LogLevel.Debug); + } + } + + public void TriggerSound(object sender, UUID objectID, UUID parentID, UUID ownerID, UUID soundID, Vector3 position, float gain) + { + if (OnTriggerSound != null) + { + OnTriggerSound(sender, objectID, parentID, ownerID, soundID, position, gain); + } + + SoundTriggerPacket sound = new SoundTriggerPacket(); + sound.SoundData.Handle = server.Scene.RegionHandle; + sound.SoundData.ObjectID = objectID; + sound.SoundData.ParentID = parentID; + sound.SoundData.OwnerID = ownerID; + sound.SoundData.Position = position; + sound.SoundData.SoundID = soundID; + sound.SoundData.Gain = gain; + + server.UDP.BroadcastPacket(sound, PacketCategory.State); + } + + public void TriggerEffects(object sender, ViewerEffect[] effects) + { + if (OnTriggerEffects != null) + { + OnTriggerEffects(sender, effects); + } + + ViewerEffectPacket effect = new ViewerEffectPacket(); + effect.AgentData.AgentID = UUID.Zero; + effect.AgentData.SessionID = UUID.Zero; + + effect.Effect = new ViewerEffectPacket.EffectBlock[effects.Length]; + + for (int i = 0; i < effects.Length; i++) + { + ViewerEffect currentEffect = effects[i]; + ViewerEffectPacket.EffectBlock block = new ViewerEffectPacket.EffectBlock(); + + block.AgentID = currentEffect.AgentID; + block.Color = currentEffect.Color.GetBytes(true); + block.Duration = currentEffect.Duration; + block.ID = currentEffect.EffectID; + block.Type = (byte)currentEffect.Type; + block.TypeData = currentEffect.TypeData; + + effect.Effect[i] = block; + } + + server.UDP.BroadcastPacket(effect, PacketCategory.State); + } + + public bool ContainsObject(uint localID) + { + return sceneObjects.ContainsKey(localID); + } + + public bool ContainsObject(UUID id) + { + return sceneObjects.ContainsKey(id); + } + + public int ObjectCount() + { + return sceneObjects.Count; + } + + public bool TryGetObject(uint localID, out SimulationObject obj) + { + return sceneObjects.TryGetValue(localID, out obj); + } + + public bool TryGetObject(UUID id, out SimulationObject obj) + { + return sceneObjects.TryGetValue(id, out obj); + } + + public bool AgentAdd(object sender, Agent agent, PrimFlags creatorFlags) + { + // Check if the agent already exists in the scene + lock (sceneAgents) + { + if (sceneAgents.ContainsKey(agent.ID)) + sceneAgents.Remove(agent.ID); + } + + // Avatars always have physics + agent.Avatar.Prim.Flags |= PrimFlags.Physics; + + // Default avatar values + agent.Avatar.Prim.Position = new Vector3(128f, 128f, 25f); + agent.Avatar.Prim.Rotation = Quaternion.Identity; + agent.Avatar.Prim.Scale = new Vector3(0.45f, 0.6f, 1.9f); + agent.Avatar.Prim.PrimData.Material = Material.Flesh; + agent.Avatar.Prim.PrimData.PCode = PCode.Avatar; + agent.Avatar.Prim.Textures = new Primitive.TextureEntry(new UUID("c228d1cf-4b5d-4ba8-84f4-899a0796aa97")); + + // Set the avatar name + NameValue[] name = new NameValue[2]; + name[0] = new NameValue("FirstName", NameValue.ValueType.String, NameValue.ClassType.ReadWrite, + NameValue.SendtoType.SimViewer, agent.FirstName); + name[1] = new NameValue("LastName", NameValue.ValueType.String, NameValue.ClassType.ReadWrite, + NameValue.SendtoType.SimViewer, agent.LastName); + agent.Avatar.Prim.NameValues = name; + + // Give testers a provisionary balance of 1000L + agent.Balance = 1000; + + // Some default avatar prim properties + agent.Avatar.Prim.Properties = new Primitive.ObjectProperties(); + agent.Avatar.Prim.Properties.CreationDate = Utils.UnixTimeToDateTime(agent.CreationTime); + agent.Avatar.Prim.Properties.Name = agent.FullName; + agent.Avatar.Prim.Properties.ObjectID = agent.ID; + + if (agent.Avatar.Prim.LocalID == 0) + { + // Assign a unique LocalID to this agent + agent.Avatar.Prim.LocalID = (uint)Interlocked.Increment(ref currentLocalID); + } + + if (OnAgentAdd != null) + OnAgentAdd(sender, agent, creatorFlags); + + // Add the agent to the scene dictionary + lock (sceneAgents) sceneAgents[agent.ID] = agent; + + // Send out an update to everyone + //ObjectAdd(this, agent.Avatar, agent.Avatar.Prim.OwnerID, 0, PrimFlags.None); + + return true; + } + + public void AgentAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams) + { + if (OnAgentAppearance != null) + { + OnAgentAppearance(sender, agent, textures, visualParams); + } + + // Broadcast an object update for this avatar + // TODO: Is this necessary here? + //ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(agent.Avatar, + // regionHandle, agent.Flags); + //server.UDP.BroadcastPacket(update, PacketCategory.State); + + // Update the avatar + agent.Avatar.Prim.Textures = textures; + if (visualParams != null && visualParams.Length > 1) + agent.VisualParams = visualParams; + + if (agent.VisualParams != null) + { + // Send the appearance packet to all other clients + AvatarAppearancePacket appearance = BuildAppearancePacket(agent); + ForEachAgent( + delegate(Agent recipient) + { + if (recipient != agent) + server.UDP.SendPacket(recipient.ID, appearance, PacketCategory.State); + } + ); + } + } + + public void ForEachObject(Action action) + { + sceneObjects.ForEach(action); + } + + public SimulationObject FindObject(Predicate predicate) + { + return sceneObjects.FindValue(predicate); + } + + public bool TryGetAgent(UUID id, out Agent agent) + { + return sceneAgents.TryGetValue(id, out agent); + } + + public int AgentCount() + { + return sceneAgents.Count; + } + + public void ForEachAgent(Action action) + { + lock (sceneAgents) + { + foreach (Agent agent in sceneAgents.Values) + action(agent); + } + } + + public Agent FindAgent(Predicate predicate) + { + lock (sceneAgents) + { + foreach (Agent agent in sceneAgents.Values) + { + if (predicate(agent)) + return agent; + } + } + + return null; + } + + #region Terrain and Wind + public float GetTerrainHeightAt(float fx, float fy) { int x = (int)fx; @@ -208,656 +944,7 @@ namespace Simian.Extensions windSpeeds[y, x] = windSpeed; } - public bool ObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags) - { - if (OnObjectAdd != null) - { - OnObjectAdd(sender, obj, ownerID, scriptStartParam, creatorFlags); - } - - // Check if the object already exists in the scene - SimulationObject oldObj; - if (sceneObjects.TryGetValue(obj.Prim.ID, out oldObj)) - { - sceneObjects.Remove(oldObj.Prim.LocalID, oldObj.Prim.ID); - - // Point the new object at the old undo/redo queues - obj.UndoSteps = oldObj.UndoSteps; - obj.RedoSteps = oldObj.RedoSteps; - } - else - { - // Enable some default flags that all objects will have - obj.Prim.Flags |= server.Permissions.GetDefaultObjectFlags(); - - // Object did not exist before, so there's no way it could contain inventory - obj.Prim.Flags |= PrimFlags.InventoryEmpty; - - // Fun Fact: Prim.OwnerID is only used by the LL viewer to mute sounds - obj.Prim.OwnerID = ownerID; - - // Assign a unique LocalID to this object if no LocalID is set - if (obj.Prim.LocalID == 0) - obj.Prim.LocalID = (uint)Interlocked.Increment(ref currentLocalID); - - // Assign a random ID to this object if no ID is set - if (obj.Prim.ID == UUID.Zero) - obj.Prim.ID = UUID.Random(); - - // Set the RegionHandle if no RegionHandle is set - if (obj.Prim.RegionHandle == 0) - obj.Prim.RegionHandle = server.Scene.RegionHandle; - - // Make sure this object has properties - if (obj.Prim.Properties == null) - { - obj.Prim.Properties = new Primitive.ObjectProperties(); - obj.Prim.Properties.CreationDate = DateTime.Now; - obj.Prim.Properties.CreatorID = ownerID; - obj.Prim.Properties.Name = "New Object"; - obj.Prim.Properties.ObjectID = obj.Prim.ID; - obj.Prim.Properties.OwnerID = ownerID; - obj.Prim.Properties.Permissions = server.Permissions.GetDefaultPermissions(); - obj.Prim.Properties.SalePrice = 10; - } - - // Set the default scale - if (obj.Prim.Scale == Vector3.Zero) - obj.Prim.Scale = new Vector3(0.5f, 0.5f, 0.5f); - - // Set default textures if none are set - if (obj.Prim.Textures == null) - obj.Prim.Textures = new Primitive.TextureEntry(new UUID("89556747-24cb-43ed-920b-47caed15465f")); // Plywood - } - - // Reset the prim CRC - obj.CRC = 0; - - // Add the object to the scene dictionary - sceneObjects.Add(obj.Prim.LocalID, obj.Prim.ID, obj); - - if (sceneAgents.ContainsKey(obj.Prim.OwnerID)) - { - // Send an update out to the creator - ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(obj.Prim, regionHandle, - obj.Prim.Flags | creatorFlags | PrimFlags.ObjectYouOwner, obj.CRC); - server.UDP.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State); - } - - // Send an update out to everyone else - ObjectUpdatePacket updateToOthers = SimulationObject.BuildFullUpdate(obj.Prim, regionHandle, - obj.Prim.Flags, obj.CRC); - server.Scene.ForEachAgent( - delegate(Agent recipient) - { - if (recipient.ID != obj.Prim.OwnerID) - server.UDP.SendPacket(recipient.ID, updateToOthers, PacketCategory.State); - } - ); - - return true; - } - - public bool ObjectRemove(object sender, uint localID) - { - SimulationObject obj; - Agent agent; - - if (sceneObjects.TryGetValue(localID, out obj)) - { - if (sceneAgents.TryGetValue(obj.Prim.ID, out agent)) - AgentRemove(sender, agent); - - if (OnObjectRemove != null) - OnObjectRemove(sender, obj); - - sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID); - - KillObjectPacket kill = new KillObjectPacket(); - kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; - kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); - kill.ObjectData[0].ID = obj.Prim.LocalID; - - server.UDP.BroadcastPacket(kill, PacketCategory.State); - return true; - } - - return false; - } - - public bool ObjectRemove(object sender, UUID id) - { - SimulationObject obj; - Agent agent; - - if (sceneAgents.TryGetValue(id, out agent)) - AgentRemove(sender, agent); - - if (sceneObjects.TryGetValue(id, out obj)) - { - if (OnObjectRemove != null) - OnObjectRemove(sender, obj); - - sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID); - - KillObjectPacket kill = new KillObjectPacket(); - kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; - kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); - kill.ObjectData[0].ID = obj.Prim.LocalID; - - server.UDP.BroadcastPacket(kill, PacketCategory.State); - return true; - } - - return false; - } - - void AgentRemove(object sender, Agent agent) - { - if (OnAgentRemove != null) - OnAgentRemove(sender, agent); - - Logger.Log("Removing agent " + agent.FullName + " from the scene", Helpers.LogLevel.Info); - - sceneAgents.Remove(agent.ID); - - KillObjectPacket kill = new KillObjectPacket(); - kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1]; - kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock(); - kill.ObjectData[0].ID = agent.Avatar.Prim.LocalID; - - server.UDP.BroadcastPacket(kill, PacketCategory.State); - - // Kill the EventQueue - RemoveEventQueue(agent.ID); - - // Remove the UDP client - server.UDP.RemoveClient(agent); - - // Notify everyone in the scene that this agent has gone offline - OfflineNotificationPacket offline = new OfflineNotificationPacket(); - offline.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[1]; - offline.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock(); - offline.AgentBlock[0].AgentID = agent.ID; - server.UDP.BroadcastPacket(offline, PacketCategory.State); - } - - public void ObjectTransform(object sender, SimulationObject obj, Vector3 position, Quaternion rotation, - Vector3 velocity, Vector3 acceleration, Vector3 angularVelocity) - { - if (OnObjectTransform != null) - { - OnObjectTransform(sender, obj, position, rotation, velocity, acceleration, angularVelocity); - } - - // Add an undo step for prims (not avatars) - if (!(obj.Prim is Avatar)) - obj.CreateUndoStep(); - - // Update the object - obj.Prim.Position = position; - obj.Prim.Rotation = rotation; - obj.Prim.Velocity = velocity; - obj.Prim.Acceleration = acceleration; - obj.Prim.AngularVelocity = angularVelocity; - - // Reset the prim CRC - obj.CRC = 0; - - // Inform clients - BroadcastObjectUpdate(obj.Prim); - } - - public void ObjectFlags(object sender, SimulationObject obj, PrimFlags flags) - { - if (OnObjectFlags != null) - { - OnObjectFlags(sender, obj, flags); - } - - // Add an undo step for prims (not avatars) - if (!(obj.Prim is Avatar)) - obj.CreateUndoStep(); - - // Update the object - obj.Prim.Flags = flags; - - // Reset the prim CRC - obj.CRC = 0; - - // Inform clients - BroadcastObjectUpdate(obj.Prim); - } - - public void ObjectModify(object sender, SimulationObject obj, Primitive.ConstructionData data) - { - if (OnObjectModify != null) - { - OnObjectModify(sender, obj, data); - } - - // Add an undo step for prims (not avatars) - if (!(obj.Prim is Avatar)) - obj.CreateUndoStep(); - - // Update the object - obj.Prim.PrimData = data; - - // Reset the prim CRC - obj.CRC = 0; - - // Inform clients - BroadcastObjectUpdate(obj.Prim); - } - - public void ObjectModifyTextures(object sender, SimulationObject obj, string mediaURL, Primitive.TextureEntry textureEntry) - { - if (OnObjectModifyTextures != null) - { - OnObjectModifyTextures(sender, obj, mediaURL, textureEntry); - } - - // Add an undo step for prims (not avatars) - if (!(obj.Prim is Avatar)) - obj.CreateUndoStep(); - - // Update the object - obj.Prim.Textures = textureEntry; - obj.Prim.MediaURL = mediaURL; - - // Reset the prim CRC - obj.CRC = 0; - - // Inform clients - BroadcastObjectUpdate(obj.Prim); - } - - public void ObjectSetRotationAxis(object sender, SimulationObject obj, Vector3 rotationAxis) - { - if (OnObjectSetRotationAxis != null) - { - OnObjectSetRotationAxis(sender, obj, rotationAxis); - } - - // Update the object - obj.RotationAxis = rotationAxis; - } - - public void ObjectApplyImpulse(object sender, SimulationObject obj, Vector3 impulse) - { - if (OnObjectApplyImpulse != null) - { - OnObjectApplyImpulse(sender, obj, impulse); - } - - // FIXME: - } - - public void ObjectApplyRotationalImpulse(object sender, SimulationObject obj, Vector3 impulse) - { - if (OnObjectApplyRotationalImpulse != null) - { - OnObjectApplyRotationalImpulse(sender, obj, impulse); - } - - // FIXME: - } - - public void ObjectSetTorque(object sender, SimulationObject obj, Vector3 torque) - { - if (OnObjectSetTorque != null) - { - OnObjectSetTorque(sender, obj, torque); - } - - obj.Torque = torque; - } - - public void ObjectAnimate(object sender, UUID senderID, UUID objectID, AnimationTrigger[] animations) - { - if (OnObjectAnimate != null) - { - OnObjectAnimate(sender, senderID, objectID, animations); - } - - AvatarAnimationPacket sendAnim = new AvatarAnimationPacket(); - sendAnim.Sender.ID = senderID; - sendAnim.AnimationSourceList = new AvatarAnimationPacket.AnimationSourceListBlock[1]; - sendAnim.AnimationSourceList[0] = new AvatarAnimationPacket.AnimationSourceListBlock(); - sendAnim.AnimationSourceList[0].ObjectID = objectID; - - sendAnim.AnimationList = new AvatarAnimationPacket.AnimationListBlock[animations.Length]; - for (int i = 0; i < animations.Length; i++) - { - sendAnim.AnimationList[i] = new AvatarAnimationPacket.AnimationListBlock(); - sendAnim.AnimationList[i].AnimID = animations[i].AnimationID; - sendAnim.AnimationList[i].AnimSequenceID = animations[i].SequenceID; - } - - server.UDP.BroadcastPacket(sendAnim, PacketCategory.State); - } - - public void ObjectChat(object sender, UUID ownerID, UUID sourceID, ChatAudibleLevel audible, ChatType type, ChatSourceType sourceType, - string fromName, Vector3 position, int channel, string message) - { - if (OnObjectChat != null) - { - OnObjectChat(sender, ownerID, sourceID, audible, type, sourceType, fromName, position, channel, message); - } - - if (channel == 0) - { - // TODO: Reduction provider will impose the chat radius - ChatFromSimulatorPacket chat = new ChatFromSimulatorPacket(); - chat.ChatData.Audible = (byte)audible; - chat.ChatData.ChatType = (byte)type; - chat.ChatData.OwnerID = ownerID; - chat.ChatData.SourceID = sourceID; - chat.ChatData.SourceType = (byte)sourceType; - chat.ChatData.Position = position; - chat.ChatData.FromName = Utils.StringToBytes(fromName); - chat.ChatData.Message = Utils.StringToBytes(message); - - server.UDP.BroadcastPacket(chat, PacketCategory.Messaging); - } - } - - public void ObjectUndo(object sender, SimulationObject obj) - { - if (OnObjectUndo != null) - { - OnObjectUndo(sender, obj); - } - - Primitive prim = obj.UndoSteps.DequeueLast(); - if (prim != null) - { - Logger.Log(String.Format("Performing undo on object {0}", obj.Prim.ID), Helpers.LogLevel.Debug); - - obj.RedoSteps.Enqueue(prim); - obj.Prim = prim; - - // Inform clients - BroadcastObjectUpdate(obj.Prim); - } - else - { - Logger.Log(String.Format("Undo requested on object {0} with no remaining undo steps", obj.Prim.ID), - Helpers.LogLevel.Debug); - } - } - - public void ObjectRedo(object sender, SimulationObject obj) - { - if (OnObjectRedo != null) - { - OnObjectRedo(sender, obj); - } - - Primitive prim = obj.RedoSteps.DequeueLast(); - if (prim != null) - { - Logger.Log(String.Format("Performing redo on object {0}", obj.Prim.ID), Helpers.LogLevel.Debug); - - obj.UndoSteps.Enqueue(prim); - obj.Prim = prim; - - // Inform clients - BroadcastObjectUpdate(obj.Prim); - } - else - { - Logger.Log(String.Format("Redo requested on object {0} with no remaining redo steps", obj.Prim.ID), - Helpers.LogLevel.Debug); - } - } - - public void TriggerSound(object sender, UUID objectID, UUID parentID, UUID ownerID, UUID soundID, Vector3 position, float gain) - { - if (OnTriggerSound != null) - { - OnTriggerSound(sender, objectID, parentID, ownerID, soundID, position, gain); - } - - SoundTriggerPacket sound = new SoundTriggerPacket(); - sound.SoundData.Handle = server.Scene.RegionHandle; - sound.SoundData.ObjectID = objectID; - sound.SoundData.ParentID = parentID; - sound.SoundData.OwnerID = ownerID; - sound.SoundData.Position = position; - sound.SoundData.SoundID = soundID; - sound.SoundData.Gain = gain; - - server.UDP.BroadcastPacket(sound, PacketCategory.State); - } - - public void TriggerEffects(object sender, ViewerEffect[] effects) - { - if (OnTriggerEffects != null) - { - OnTriggerEffects(sender, effects); - } - - ViewerEffectPacket effect = new ViewerEffectPacket(); - effect.AgentData.AgentID = UUID.Zero; - effect.AgentData.SessionID = UUID.Zero; - - effect.Effect = new ViewerEffectPacket.EffectBlock[effects.Length]; - - for (int i = 0; i < effects.Length; i++) - { - ViewerEffect currentEffect = effects[i]; - ViewerEffectPacket.EffectBlock block = new ViewerEffectPacket.EffectBlock(); - - block.AgentID = currentEffect.AgentID; - block.Color = currentEffect.Color.GetBytes(true); - block.Duration = currentEffect.Duration; - block.ID = currentEffect.EffectID; - block.Type = (byte)currentEffect.Type; - block.TypeData = currentEffect.TypeData; - - effect.Effect[i] = block; - } - - server.UDP.BroadcastPacket(effect, PacketCategory.State); - } - - public bool ContainsObject(uint localID) - { - return sceneObjects.ContainsKey(localID); - } - - public bool ContainsObject(UUID id) - { - return sceneObjects.ContainsKey(id); - } - - public int ObjectCount() - { - return sceneObjects.Count; - } - - public bool TryGetObject(uint localID, out SimulationObject obj) - { - return sceneObjects.TryGetValue(localID, out obj); - } - - public bool TryGetObject(UUID id, out SimulationObject obj) - { - return sceneObjects.TryGetValue(id, out obj); - } - - public bool AgentAdd(object sender, Agent agent, PrimFlags creatorFlags) - { - // Check if the agent already exists in the scene - if (sceneAgents.ContainsKey(agent.ID)) - sceneAgents.Remove(agent.ID); - - // Avatars always have physics - agent.Avatar.Prim.Flags |= PrimFlags.Physics; - - // Default avatar values - agent.Avatar.Prim.Position = new Vector3(128f, 128f, 25f); - agent.Avatar.Prim.Rotation = Quaternion.Identity; - agent.Avatar.Prim.Scale = new Vector3(0.45f, 0.6f, 1.9f); - agent.Avatar.Prim.PrimData.Material = Material.Flesh; - agent.Avatar.Prim.PrimData.PCode = PCode.Avatar; - agent.Avatar.Prim.Textures = new Primitive.TextureEntry(new UUID("c228d1cf-4b5d-4ba8-84f4-899a0796aa97")); - - // Set the avatar name - NameValue[] name = new NameValue[2]; - name[0] = new NameValue("FirstName", NameValue.ValueType.String, NameValue.ClassType.ReadWrite, - NameValue.SendtoType.SimViewer, agent.FirstName); - name[1] = new NameValue("LastName", NameValue.ValueType.String, NameValue.ClassType.ReadWrite, - NameValue.SendtoType.SimViewer, agent.LastName); - agent.Avatar.Prim.NameValues = name; - - // Give testers a provisionary balance of 1000L - agent.Balance = 1000; - - // Some default avatar prim properties - agent.Avatar.Prim.Properties = new Primitive.ObjectProperties(); - agent.Avatar.Prim.Properties.CreationDate = Utils.UnixTimeToDateTime(agent.CreationTime); - agent.Avatar.Prim.Properties.Name = agent.FullName; - agent.Avatar.Prim.Properties.ObjectID = agent.ID; - - if (agent.Avatar.Prim.LocalID == 0) - { - // Assign a unique LocalID to this agent - agent.Avatar.Prim.LocalID = (uint)Interlocked.Increment(ref currentLocalID); - } - - if (OnAgentAdd != null) - OnAgentAdd(sender, agent, creatorFlags); - - // Add the agent to the scene dictionary - sceneAgents[agent.ID] = agent; - - // Send out an update to everyone - //ObjectAdd(this, agent.Avatar, agent.Avatar.Prim.OwnerID, 0, PrimFlags.None); - - return true; - } - - public void AgentAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams) - { - if (OnAgentAppearance != null) - { - OnAgentAppearance(sender, agent, textures, visualParams); - } - - // Broadcast an object update for this avatar - // TODO: Is this necessary here? - //ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(agent.Avatar, - // regionHandle, agent.Flags); - //server.UDP.BroadcastPacket(update, PacketCategory.State); - - // Update the avatar - agent.Avatar.Prim.Textures = textures; - if (visualParams != null && visualParams.Length > 1) - agent.VisualParams = visualParams; - - if (agent.VisualParams != null) - { - // Send the appearance packet to all other clients - AvatarAppearancePacket appearance = BuildAppearancePacket(agent); - ForEachAgent( - delegate(Agent recipient) - { - if (recipient != agent) - server.UDP.SendPacket(recipient.ID, appearance, PacketCategory.State); - } - ); - } - } - - public void ForEachObject(Action action) - { - sceneObjects.ForEach(action); - } - - public SimulationObject FindObject(Predicate predicate) - { - return sceneObjects.FindValue(predicate); - } - - public bool TryGetAgent(UUID id, out Agent agent) - { - return sceneAgents.TryGetValue(id, out agent); - } - - public int AgentCount() - { - return sceneAgents.Count; - } - - public void ForEachAgent(Action action) - { - lock (sceneAgents) - { - foreach (Agent agent in sceneAgents.Values) - action(agent); - } - } - - public Agent FindAgent(Predicate predicate) - { - lock (sceneAgents) - { - foreach (Agent agent in sceneAgents.Values) - { - if (predicate(agent)) - return agent; - } - } - - return null; - } - - public bool SeedCapabilityHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state) - { - UUID agentID = (UUID)state; - - OSDArray array = OSDParser.DeserializeLLSDXml(request.Body) as OSDArray; - if (array != null) - { - OSDMap osdResponse = new OSDMap(); - - for (int i = 0; i < array.Count; i++) - { - string capName = array[i].AsString(); - - switch (capName) - { - case "EventQueueGet": - Uri eqCap = null; - - // Check if this agent already has an event queue - EventQueueServerCap eqServer; - if (eventQueues.TryGetValue(agentID, out eqServer)) - eqCap = eqServer.Capability; - - // If not, create one - if (eqCap == null) - eqCap = CreateEventQueue(agentID); - - osdResponse.Add("EventQueueGet", OSD.FromUri(eqCap)); - break; - } - } - - byte[] responseData = OSDParser.SerializeLLSDXmlBytes(osdResponse); - response.ContentLength = responseData.Length; - response.Body.Write(responseData, 0, responseData.Length); - response.Body.Flush(); - } - else - { - response.Status = HttpStatusCode.BadRequest; - } - - return true; - } + #endregion Terrain and Wind public Uri CreateEventQueue(UUID agentID) { @@ -907,52 +994,83 @@ namespace Simian.Extensions } } + public bool SeedCapabilityHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state) + { + UUID agentID = (UUID)state; + + OSDArray array = OSDParser.DeserializeLLSDXml(request.Body) as OSDArray; + if (array != null) + { + OSDMap osdResponse = new OSDMap(); + + for (int i = 0; i < array.Count; i++) + { + string capName = array[i].AsString(); + + switch (capName) + { + case "EventQueueGet": + Uri eqCap = null; + + // Check if this agent already has an event queue + EventQueueServerCap eqServer; + if (eventQueues.TryGetValue(agentID, out eqServer)) + eqCap = eqServer.Capability; + + // If not, create one + if (eqCap == null) + eqCap = CreateEventQueue(agentID); + + osdResponse.Add("EventQueueGet", OSD.FromUri(eqCap)); + break; + } + } + + byte[] responseData = OSDParser.SerializeLLSDXmlBytes(osdResponse); + response.ContentLength = responseData.Length; + response.Body.Write(responseData, 0, responseData.Length); + response.Body.Flush(); + } + else + { + response.Status = HttpStatusCode.BadRequest; + } + + return true; + } + bool EventQueueHandler(IHttpClientContext context, IHttpRequest request, IHttpResponse response, object state) { EventQueueServer eqServer = (EventQueueServer)state; return eqServer.EventQueueHandler(context, request, response); } - // FIXME: This function needs to go away as soon as we stop sending full object updates for everything - void BroadcastObjectUpdate(Primitive prim) - { - SimulationObject obj; - if (TryGetObject(prim.ID, out obj)) - ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None); - } - void CompleteAgentMovementHandler(Packet packet, Agent agent) { // Add this avatar as an object in the scene - if (ObjectAddOrUpdate(this, agent.Avatar, agent.Avatar.Prim.OwnerID, 0, PrimFlags.None)) - { - // Send a response back to the client - AgentMovementCompletePacket complete = new AgentMovementCompletePacket(); - complete.AgentData.AgentID = agent.ID; - complete.AgentData.SessionID = agent.SessionID; - complete.Data.LookAt = Vector3.UnitX; - complete.Data.Position = agent.Avatar.Prim.Position; - complete.Data.RegionHandle = regionHandle; - complete.Data.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now); - complete.SimData.ChannelVersion = Utils.StringToBytes("Simian"); + ObjectAddOrUpdate(this, agent.Avatar, agent.Avatar.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.FullUpdate); - server.UDP.SendPacket(agent.ID, complete, PacketCategory.Transaction); + // Send a response back to the client + AgentMovementCompletePacket complete = new AgentMovementCompletePacket(); + complete.AgentData.AgentID = agent.ID; + complete.AgentData.SessionID = agent.SessionID; + complete.Data.LookAt = Vector3.UnitX; + complete.Data.Position = agent.Avatar.Prim.Position; + complete.Data.RegionHandle = regionHandle; + complete.Data.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now); + complete.SimData.ChannelVersion = Utils.StringToBytes("Simian"); - // Send updates and appearances for every avatar to this new avatar - SynchronizeStateTo(agent); + server.UDP.SendPacket(agent.ID, complete, PacketCategory.Transaction); - //HACK: Notify everyone when someone logs on to the simulator - OnlineNotificationPacket online = new OnlineNotificationPacket(); - online.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[1]; - online.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock(); - online.AgentBlock[0].AgentID = agent.ID; - server.UDP.BroadcastPacket(online, PacketCategory.State); - } - else - { - Logger.Log("Received a CompleteAgentMovement but failed to insert avatar into the scene: " + - agent.FullName, Helpers.LogLevel.Warning); - } + // Send updates and appearances for every avatar to this new avatar + SynchronizeStateTo(agent); + + //HACK: Notify everyone when someone logs on to the simulator + OnlineNotificationPacket online = new OnlineNotificationPacket(); + online.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[1]; + online.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock(); + online.AgentBlock[0].AgentID = agent.ID; + server.UDP.BroadcastPacket(online, PacketCategory.State); } // HACK: The reduction provider will deprecate this at some point @@ -1069,7 +1187,7 @@ namespace Simian.Extensions static AvatarAppearancePacket BuildAppearancePacket(Agent agent) { AvatarAppearancePacket appearance = new AvatarAppearancePacket(); - appearance.ObjectData.TextureEntry = agent.Avatar.Prim.Textures.ToBytes(); + appearance.ObjectData.TextureEntry = agent.Avatar.Prim.Textures.GetBytes(); appearance.Sender.ID = agent.ID; appearance.Sender.IsTrial = false; diff --git a/Programs/Simian/Extensions/ScriptApi.cs b/Programs/Simian/Extensions/ScriptApi.cs index a563aafd..b6441e7c 100644 --- a/Programs/Simian/Extensions/ScriptApi.cs +++ b/Programs/Simian/Extensions/ScriptApi.cs @@ -942,22 +942,22 @@ namespace Simian.Extensions if ((status & ScriptTypes.STATUS_PHYSICS) == ScriptTypes.STATUS_PHYSICS) { if (value == 1) - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags | PrimFlags.Physics); + hostObject.Prim.Flags |= PrimFlags.Physics; else - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags & ~PrimFlags.Physics); + hostObject.Prim.Flags &= ~PrimFlags.Physics; } if ((status & ScriptTypes.STATUS_PHANTOM) == ScriptTypes.STATUS_PHANTOM) { if (value == 1) - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags | PrimFlags.Phantom); + hostObject.Prim.Flags |= PrimFlags.Phantom; else - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags & ~PrimFlags.Phantom); + hostObject.Prim.Flags &= ~PrimFlags.Phantom; } if ((status & ScriptTypes.STATUS_CAST_SHADOWS) == ScriptTypes.STATUS_CAST_SHADOWS) { - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags | PrimFlags.CastShadows); + hostObject.Prim.Flags |= PrimFlags.CastShadows; } if ((status & ScriptTypes.STATUS_ROTATE_X) == ScriptTypes.STATUS_ROTATE_X) @@ -983,25 +983,25 @@ namespace Simian.Extensions if ((status & ScriptTypes.STATUS_DIE_AT_EDGE) == ScriptTypes.STATUS_DIE_AT_EDGE) { if (value == 1) - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags | PrimFlags.DieAtEdge); + hostObject.Prim.Flags |= PrimFlags.DieAtEdge; else - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags & ~PrimFlags.DieAtEdge); + hostObject.Prim.Flags &= ~PrimFlags.DieAtEdge; } if ((status & ScriptTypes.STATUS_RETURN_AT_EDGE) == ScriptTypes.STATUS_RETURN_AT_EDGE) { if (value == 1) - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags | PrimFlags.ReturnAtEdge); + hostObject.Prim.Flags |= PrimFlags.ReturnAtEdge; else - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags & ~PrimFlags.ReturnAtEdge); + hostObject.Prim.Flags &= ~PrimFlags.ReturnAtEdge; } if ((status & ScriptTypes.STATUS_SANDBOX) == ScriptTypes.STATUS_SANDBOX) { if (value == 1) - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags | PrimFlags.Sandbox); + hostObject.Prim.Flags |= PrimFlags.Sandbox; else - server.Scene.ObjectFlags(this, hostObject, hostObject.Prim.Flags & ~PrimFlags.Sandbox); + hostObject.Prim.Flags &= ~PrimFlags.Sandbox; } if (statusrotationaxis != 0) @@ -1021,6 +1021,8 @@ namespace Simian.Extensions server.Scene.ObjectSetRotationAxis(this, parent, rotationAxis); } + + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimFlags); } public LSL_Integer llGetStatus(int status) @@ -1076,7 +1078,7 @@ namespace Simian.Extensions // TODO: Apply constraints hostObject.Prim.Scale = new Vector3((float)scale.x, (float)scale.y, (float)scale.z); - server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.Scale); } public LSL_Vector llGetScale() @@ -1090,7 +1092,7 @@ namespace Simian.Extensions hostObject.AddScriptLPS(1); hostObject.Prim.ClickAction = (ClickAction)action; - server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.ClickAction); } public void llSetColor(LSL_Vector color, int face) @@ -1255,8 +1257,8 @@ namespace Simian.Extensions // Child prims do not have velocity, only parents SimulationObject parent = hostObject.GetLinksetParent(); - server.Scene.ObjectTransform(this, parent, parent.Prim.Position, parent.Prim.Rotation, velocity, parent.Prim.Acceleration, - parent.Prim.AngularVelocity); + parent.Prim.Velocity = velocity; + server.Scene.ObjectAddOrUpdate(this, parent, parent.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Velocity); } public LSL_Vector llGetForce() @@ -1459,28 +1461,55 @@ namespace Simian.Extensions hostObject.Prim.Sound = KeyOrName(sound); hostObject.Prim.SoundGain = (float)volume; - hostObject.Prim.SoundFlags = 1; // TODO: ??? - hostObject.Prim.SoundRadius = 20; // TODO: Randomly selected + hostObject.Prim.SoundFlags = SoundFlags.Loop; + hostObject.Prim.SoundRadius = 20f; // TODO: Randomly selected - server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.Sound); } public void llLoopSoundMaster(string sound, double volume) { hostObject.AddScriptLPS(1); - NotImplemented("llLoopSoundMaster"); + + if (hostObject.Prim.Sound != UUID.Zero) + llStopSound(); + + hostObject.Prim.Sound = KeyOrName(sound); + hostObject.Prim.SoundGain = (float)volume; + hostObject.Prim.SoundFlags = SoundFlags.Loop | SoundFlags.SyncMaster; + hostObject.Prim.SoundRadius = 20f; // TODO: Randomly selected + + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.Sound); } public void llLoopSoundSlave(string sound, double volume) { hostObject.AddScriptLPS(1); - NotImplemented("llLoopSoundSlave"); + + if (hostObject.Prim.Sound != UUID.Zero) + llStopSound(); + + hostObject.Prim.Sound = KeyOrName(sound); + hostObject.Prim.SoundGain = (float)volume; + hostObject.Prim.SoundFlags = SoundFlags.Loop | SoundFlags.SyncSlave; + hostObject.Prim.SoundRadius = 20f; // TODO: Randomly selected + + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.Sound); } public void llPlaySoundSlave(string sound, double volume) { hostObject.AddScriptLPS(1); - NotImplemented("llPlaySoundSlave"); + + if (hostObject.Prim.Sound != UUID.Zero) + llStopSound(); + + hostObject.Prim.Sound = KeyOrName(sound); + hostObject.Prim.SoundGain = (float)volume; + hostObject.Prim.SoundFlags = SoundFlags.SyncSlave; + hostObject.Prim.SoundRadius = 20f; // TODO: Randomly selected + + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.Sound); } public void llTriggerSound(string sound, double volume) @@ -1498,17 +1527,26 @@ namespace Simian.Extensions hostObject.AddScriptLPS(1); hostObject.Prim.Sound = UUID.Zero; - hostObject.Prim.SoundGain = 0; - hostObject.Prim.SoundFlags = 0; - hostObject.Prim.SoundRadius = 0; + hostObject.Prim.SoundGain = 0f; + hostObject.Prim.SoundFlags = SoundFlags.None; + hostObject.Prim.SoundRadius = 0f; - server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.Sound); } public void llPreloadSound(string sound) { hostObject.AddScriptLPS(1); - NotImplemented("llPreloadSound"); + + if (hostObject.Prim.Sound != UUID.Zero) + llStopSound(); + + hostObject.Prim.Sound = KeyOrName(sound); + hostObject.Prim.SoundGain = 0f; + hostObject.Prim.SoundFlags = SoundFlags.Queue; // TODO: Is this correct? + hostObject.Prim.SoundRadius = 20f; // TODO: Randomly selected + + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.Sound); // ScriptSleep(1000); } @@ -1838,11 +1876,10 @@ namespace Simian.Extensions newObj.Prim.Rotation = llrot; } - if (server.Scene.ObjectAddOrUpdate(this, newObj, hostObject.Prim.Properties.OwnerID, param, PrimFlags.None) && - newObj.Prim.ParentID == 0) - { + server.Scene.ObjectAddOrUpdate(this, newObj, hostObject.Prim.Properties.OwnerID, param, PrimFlags.None, UpdateFlags.FullUpdate); + + if (newObj.Prim.ParentID == 0) newParent = newObj; - } } if (newParent != null) @@ -2064,8 +2101,9 @@ namespace Simian.Extensions if (targetHeight > 0f) newPosition.Z = targetHeight; - server.Scene.ObjectTransform(this, parent, newPosition, parent.Prim.Rotation, parent.Prim.Velocity, - parent.Prim.Acceleration, parent.Prim.AngularVelocity); + parent.Prim.Position = newPosition; + // Don't set UpdateFlags.PrimFlags since Flying is a server-side only flag + server.Scene.ObjectAddOrUpdate(this, parent, parent.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position); } public void llStopHover() @@ -2075,8 +2113,9 @@ namespace Simian.Extensions SimulationObject parent = hostObject.GetLinksetParent(); parent.Prim.Flags &= ~PrimFlags.Flying; - server.Scene.ObjectTransform(this, parent, parent.Prim.Position, parent.Prim.Rotation, parent.Prim.Velocity, - parent.Prim.Acceleration, parent.Prim.AngularVelocity); + // Trigger an object transformation even though we don't directly manipulate any parameters that are sent + // to the client. This will make sure the physics engine realizes the flying flag has been turned off + server.Scene.ObjectAddOrUpdate(this, parent, parent.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position); } public void llMinEventDelay(double delay) @@ -2184,8 +2223,8 @@ namespace Simian.Extensions Vector3 angVel = new Vector3((float)axis.x, (float)axis.y, (float)axis.z); angVel *= (float)spinrate; - server.Scene.ObjectTransform(this, hostObject, hostObject.Prim.Position, hostObject.Prim.Rotation, - hostObject.Prim.Velocity, hostObject.Prim.Acceleration, angVel); + hostObject.Prim.AngularVelocity = angVel; + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.AngularVelocity); } public LSL_Integer llGetStartParameter() @@ -2510,7 +2549,7 @@ namespace Simian.Extensions (float)Utils.Clamp(alpha, 0f, 1f)); hostObject.Prim.Text = text; - server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.Text); } public LSL_Float llWater(LSL_Vector offset) @@ -3406,7 +3445,7 @@ namespace Simian.Extensions pTexAnim.Start = (float)start; hostObject.Prim.TextureAnim = pTexAnim; - server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.TextureAnim); } public void llTriggerSoundLimited(string sound, double volume, LSL_Vector top_north_east, LSL_Vector bottom_south_west) @@ -3754,7 +3793,7 @@ namespace Simian.Extensions hostObject.Prim.ParticleSys = prules; } - server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, hostObject, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.Particles); } public void llGroundRepel(double height, int water, double tau) @@ -5158,7 +5197,7 @@ namespace Simian.Extensions texcolor.A = Utils.Clamp((float)alpha, 0.0f, 1.0f); tex.FaceTextures[face].RGBA = texcolor; - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Textures); } else if (face == ScriptTypes.ALL_SIDES) { @@ -5176,7 +5215,7 @@ namespace Simian.Extensions texcolor.A = Utils.Clamp((float)alpha, 0.0f, 1.0f); tex.DefaultTexture.RGBA = texcolor; - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Textures); } } @@ -5202,7 +5241,7 @@ namespace Simian.Extensions part.Prim.PrimData.PathCurve = PathCurve.Line; } - server.Scene.ObjectAddOrUpdate(this, part, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, part, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.ExtraData | UpdateFlags.PrimData); } private void SetPointLight(SimulationObject part, bool light, LSL_Vector color, float intensity, float radius, float falloff) @@ -5225,7 +5264,7 @@ namespace Simian.Extensions part.Prim.Light = null; } - server.Scene.ObjectAddOrUpdate(this, part, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, part, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.ExtraData); } private LSL_Vector GetColor(SimulationObject part, int face) @@ -5528,7 +5567,7 @@ namespace Simian.Extensions prim.Prim.PrimData.ProfileBegin = Primitive.UnpackBeginCut((ushort)(50000.0 * dimple.x)); prim.Prim.PrimData.ProfileEnd = Primitive.UnpackBeginCut((ushort)(50000.0 * (1.0 - dimple.y))); - server.Scene.ObjectAddOrUpdate(this, prim, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, prim, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimData); } private void SetPrimitiveShapeParams(SimulationObject prim, int holeshape, LSL_Vector cut, float hollow, LSL_Vector twist, @@ -5919,10 +5958,11 @@ namespace Simian.Extensions string ph = rules.Data[idx++].ToString(); if (ph.Equals("1")) - server.Scene.ObjectFlags(this, parent, parent.Prim.Flags | PrimFlags.Phantom); + parent.Prim.Flags |= PrimFlags.Phantom; else - server.Scene.ObjectFlags(this, parent, parent.Prim.Flags & ~PrimFlags.Phantom); + parent.Prim.Flags &= ~PrimFlags.Phantom; + server.Scene.ObjectAddOrUpdate(this, parent, parent.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimFlags); break; case ScriptTypes.PRIM_PHYSICS: if (remain < 1) return; @@ -5930,10 +5970,11 @@ namespace Simian.Extensions string phy = rules.Data[idx++].ToString(); if (phy.Equals("1")) - server.Scene.ObjectFlags(this, parent, parent.Prim.Flags & ~PrimFlags.Phantom); + parent.Prim.Flags |= PrimFlags.Phantom; else - server.Scene.ObjectFlags(this, parent, parent.Prim.Flags & ~PrimFlags.Physics); + parent.Prim.Flags &= ~PrimFlags.Physics; + server.Scene.ObjectAddOrUpdate(this, parent, parent.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimFlags); break; case ScriptTypes.PRIM_TEMP_ON_REZ: if (remain < 1) return; @@ -5941,10 +5982,11 @@ namespace Simian.Extensions string temp = rules.Data[idx++].ToString(); if (temp.Equals("1")) - server.Scene.ObjectFlags(this, parent, parent.Prim.Flags & ~PrimFlags.TemporaryOnRez); + parent.Prim.Flags |= PrimFlags.TemporaryOnRez; else - server.Scene.ObjectFlags(this, parent, parent.Prim.Flags & ~PrimFlags.TemporaryOnRez); + parent.Prim.Flags &= ~PrimFlags.TemporaryOnRez; + server.Scene.ObjectAddOrUpdate(this, parent, parent.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimFlags); break; } } @@ -5959,7 +6001,7 @@ namespace Simian.Extensions return; part.Prim.Scale = new Vector3((float)scale.x, (float)scale.y, (float)scale.z); - server.Scene.ObjectAddOrUpdate(this, part, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None); + server.Scene.ObjectAddOrUpdate(this, part, hostObject.Prim.Properties.OwnerID, 0, PrimFlags.None, UpdateFlags.Scale); } private void SetColor(SimulationObject part, LSL_Vector color, int face) @@ -5974,11 +6016,15 @@ namespace Simian.Extensions texcolor.G = Utils.Clamp((float)color.y, 0.0f, 1.0f); texcolor.B = Utils.Clamp((float)color.z, 0.0f, 1.0f); tex.FaceTextures[face].RGBA = texcolor; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } else if (face == ScriptTypes.ALL_SIDES) { + texcolor = tex.DefaultTexture.RGBA; + texcolor.R = Utils.Clamp((float)color.x, 0.0f, 1.0f); + texcolor.G = Utils.Clamp((float)color.y, 0.0f, 1.0f); + texcolor.B = Utils.Clamp((float)color.z, 0.0f, 1.0f); + tex.DefaultTexture.RGBA = texcolor; + for (uint i = 0; i < GetNumberOfSides(part); i++) { if (tex.FaceTextures[i] != null) @@ -5989,15 +6035,11 @@ namespace Simian.Extensions texcolor.B = Utils.Clamp((float)color.z, 0.0f, 1.0f); tex.FaceTextures[i].RGBA = texcolor; } - texcolor = tex.DefaultTexture.RGBA; - texcolor.R = Utils.Clamp((float)color.x, 0.0f, 1.0f); - texcolor.G = Utils.Clamp((float)color.y, 0.0f, 1.0f); - texcolor.B = Utils.Clamp((float)color.z, 0.0f, 1.0f); - tex.DefaultTexture.RGBA = texcolor; } - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } + + part.Prim.Textures = tex; + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Textures); } public void SetGlow(SimulationObject part, int face, float glow) @@ -6008,26 +6050,24 @@ namespace Simian.Extensions { tex.CreateFace((uint)face); tex.FaceTextures[face].Glow = glow; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } else if (face == ScriptTypes.ALL_SIDES) { + tex.DefaultTexture.Glow = glow; + for (uint i = 0; i < GetNumberOfSides(part); i++) { if (tex.FaceTextures[i] != null) tex.FaceTextures[i].Glow = glow; - - tex.DefaultTexture.Glow = glow; } - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } + + part.Prim.Textures = tex; + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Textures); } public void SetShiny(SimulationObject part, int face, int shiny, Bumpiness bump) { - Shininess sval = new Shininess(); switch (shiny) @@ -6056,11 +6096,12 @@ namespace Simian.Extensions tex.CreateFace((uint)face); tex.FaceTextures[face].Shiny = sval; tex.FaceTextures[face].Bump = bump; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } else if (face == ScriptTypes.ALL_SIDES) { + tex.DefaultTexture.Shiny = sval; + tex.DefaultTexture.Bump = bump; + for (uint i = 0; i < GetNumberOfSides(part); i++) { if (tex.FaceTextures[i] != null) @@ -6068,12 +6109,11 @@ namespace Simian.Extensions tex.FaceTextures[i].Shiny = sval; tex.FaceTextures[i].Bump = bump; ; } - tex.DefaultTexture.Shiny = sval; - tex.DefaultTexture.Bump = bump; } - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } + + part.Prim.Textures = tex; + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Textures); } public void SetFullBright(SimulationObject part, int face, bool bright) @@ -6084,11 +6124,11 @@ namespace Simian.Extensions { tex.CreateFace((uint)face); tex.FaceTextures[face].Fullbright = bright; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } else if (face == ScriptTypes.ALL_SIDES) { + tex.DefaultTexture.Fullbright = bright; + for (uint i = 0; i < GetNumberOfSides(part); i++) { if (tex.FaceTextures[i] != null) @@ -6096,11 +6136,10 @@ namespace Simian.Extensions tex.FaceTextures[i].Fullbright = bright; } } - - tex.DefaultTexture.Fullbright = bright; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } + + part.Prim.Textures = tex; + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Textures); } private void SetTexture(SimulationObject part, string texture, int face) @@ -6120,11 +6159,11 @@ namespace Simian.Extensions Primitive.TextureEntryFace texface = tex.CreateFace((uint)face); texface.TextureID = textureID; tex.FaceTextures[face] = texface; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } else if (face == ScriptTypes.ALL_SIDES) { + tex.DefaultTexture.TextureID = textureID; + for (uint i = 0; i < GetNumberOfSides(part); i++) { if (tex.FaceTextures[i] != null) @@ -6132,11 +6171,10 @@ namespace Simian.Extensions tex.FaceTextures[i].TextureID = textureID; } } - - tex.DefaultTexture.TextureID = textureID; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } + + part.Prim.Textures = tex; + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Textures); } private void ScaleTexture(SimulationObject part, double u, double v, int face) @@ -6149,11 +6187,12 @@ namespace Simian.Extensions texface.RepeatU = (float)u; texface.RepeatV = (float)v; tex.FaceTextures[face] = texface; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } if (face == ScriptTypes.ALL_SIDES) { + tex.DefaultTexture.RepeatU = (float)u; + tex.DefaultTexture.RepeatV = (float)v; + for (int i = 0; i < GetNumberOfSides(part); i++) { if (tex.FaceTextures[i] != null) @@ -6162,12 +6201,10 @@ namespace Simian.Extensions tex.FaceTextures[i].RepeatV = (float)v; } } - - tex.DefaultTexture.RepeatU = (float)u; - tex.DefaultTexture.RepeatV = (float)v; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } + + part.Prim.Textures = tex; + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Textures); } private void OffsetTexture(SimulationObject part, double u, double v, int face) @@ -6180,11 +6217,12 @@ namespace Simian.Extensions texface.OffsetU = (float)u; texface.OffsetV = (float)v; tex.FaceTextures[face] = texface; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } if (face == ScriptTypes.ALL_SIDES) { + tex.DefaultTexture.OffsetU = (float)u; + tex.DefaultTexture.OffsetV = (float)v; + for (int i = 0; i < GetNumberOfSides(part); i++) { if (tex.FaceTextures[i] != null) @@ -6193,12 +6231,10 @@ namespace Simian.Extensions tex.FaceTextures[i].OffsetV = (float)v; } } - - tex.DefaultTexture.OffsetU = (float)u; - tex.DefaultTexture.OffsetV = (float)v; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } + + part.Prim.Textures = tex; + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Textures); } private void RotateTexture(SimulationObject part, double rotation, int face) @@ -6210,11 +6246,11 @@ namespace Simian.Extensions Primitive.TextureEntryFace texface = tex.CreateFace((uint)face); texface.Rotation = (float)rotation; tex.FaceTextures[face] = texface; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } if (face == ScriptTypes.ALL_SIDES) { + tex.DefaultTexture.Rotation = (float)rotation; + for (int i = 0; i < GetNumberOfSides(part); i++) { if (tex.FaceTextures[i] != null) @@ -6222,11 +6258,10 @@ namespace Simian.Extensions tex.FaceTextures[i].Rotation = (float)rotation; } } - - tex.DefaultTexture.Rotation = (float)rotation; - - server.Scene.ObjectModifyTextures(this, part, part.Prim.MediaURL, tex); } + + part.Prim.Textures = tex; + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Textures); } private void SetPos(SimulationObject part, LSL_Vector targetPos) @@ -6236,16 +6271,14 @@ namespace Simian.Extensions if (llVecDist(currentPos, targetPos) > 10.0f * SCRIPT_DISTANCE_FACTOR) targetPos = currentPos + SCRIPT_DISTANCE_FACTOR * 10.0f * llVecNorm(targetPos - currentPos); - Vector3 newPos = new Vector3((float)targetPos.x, (float)targetPos.y, (float)targetPos.z); - - server.Scene.ObjectTransform(this, part, newPos, part.Prim.Rotation, part.Prim.Velocity, part.Prim.Acceleration, - part.Prim.AngularVelocity); + part.Prim.Position = new Vector3((float)targetPos.x, (float)targetPos.y, (float)targetPos.z); + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position); } private void SetRot(SimulationObject part, Quaternion rot) { - server.Scene.ObjectTransform(this, part, part.Prim.Position, rot, part.Prim.Velocity, part.Prim.Acceleration, - part.Prim.AngularVelocity); + part.Prim.Rotation = rot; + server.Scene.ObjectAddOrUpdate(this, part, part.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Rotation); } private LSL_Vector GetTextureOffset(SimulationObject part, int face) diff --git a/Programs/Simian/Interfaces/ISceneProvider.cs b/Programs/Simian/Interfaces/ISceneProvider.cs index 8f6ec2e5..f506677c 100644 --- a/Programs/Simian/Interfaces/ISceneProvider.cs +++ b/Programs/Simian/Interfaces/ISceneProvider.cs @@ -6,6 +6,39 @@ using OpenMetaverse.StructuredData; namespace Simian { + /// + /// Specifies that fields that have been changed in a call to ISceneProvider.ObjectAddOrUpdate + /// + [Flags] + public enum UpdateFlags : uint + { + None = 0, + AttachmentPoint = 1 << 0, + Material = 1 << 1, + ClickAction = 1 << 2, + Scale = 1 << 3, + ParentID = 1 << 4, + PrimFlags = 1 << 5, + PrimData = 1 << 6, + MediaURL = 1 << 7, + ScratchPad = 1 << 8, + Textures = 1 << 9, + TextureAnim = 1 << 10, + NameValue = 1 << 11, + Position = 1 << 12, + Rotation = 1 << 13, + Velocity = 1 << 14, + Acceleration = 1 << 15, + AngularVelocity = 1 << 16, + CollisionPlane = 1 << 17, + Text = 1 << 18, + Particles = 1 << 19, + ExtraData = 1 << 20, + Sound = 1 << 21, + Joint = 1 << 22, + FullUpdate = UInt32.MaxValue + } + #region Scene related classes public class TerrainPatch @@ -52,12 +85,8 @@ namespace Simian #endregion Scene related classes - public delegate void ObjectAddCallback(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags); + public delegate void ObjectAddOrUpdateCallback(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags updateFlags); public delegate void ObjectRemoveCallback(object sender, SimulationObject obj); - public delegate void ObjectTransformCallback(object sender, SimulationObject obj, Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 acceleration, Vector3 angularVelocity); - public delegate void ObjectFlagsCallback(object sender, SimulationObject obj, PrimFlags flags); - public delegate void ObjectModifyCallback(object sender, SimulationObject obj, Primitive.ConstructionData data); - public delegate void ObjectModifyTexturesCallback(object sender, SimulationObject obj, string mediaURL, Primitive.TextureEntry textureEntry); public delegate void ObjectSetRotationAxisCallback(object sender, SimulationObject obj, Vector3 rotationAxis); public delegate void ObjectApplyImpulseCallback(object sender, SimulationObject obj, Vector3 impulse); public delegate void ObjectApplyRotationalImpulseCallback(object sender, SimulationObject obj, Vector3 impulse); @@ -76,12 +105,8 @@ namespace Simian public interface ISceneProvider { - event ObjectAddCallback OnObjectAdd; + event ObjectAddOrUpdateCallback OnObjectAddOrUpdate; event ObjectRemoveCallback OnObjectRemove; - event ObjectTransformCallback OnObjectTransform; - event ObjectFlagsCallback OnObjectFlags; - event ObjectModifyCallback OnObjectModify; - event ObjectModifyTexturesCallback OnObjectModifyTextures; event ObjectSetRotationAxisCallback OnObjectSetRotationAxis; event ObjectApplyImpulseCallback OnObjectApplyImpulse; event ObjectApplyRotationalImpulseCallback OnObjectApplyRotationalImpulse; @@ -112,13 +137,9 @@ namespace Simian uint TerrainPatchCountWidth { get; } uint TerrainPatchCountHeight { get; } - bool ObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags); + void ObjectAddOrUpdate(object sender, SimulationObject obj, UUID ownerID, int scriptStartParam, PrimFlags creatorFlags, UpdateFlags updateFlags); bool ObjectRemove(object sender, uint localID); bool ObjectRemove(object sender, UUID id); - void ObjectTransform(object sender, SimulationObject obj, Vector3 position, Quaternion rotation, Vector3 velocity, Vector3 acceleration, Vector3 angularVelocity); - void ObjectFlags(object sender, SimulationObject obj, PrimFlags flags); - void ObjectModify(object sender, SimulationObject obj, Primitive.ConstructionData data); - void ObjectModifyTextures(object sender, SimulationObject obj, string mediaURL, Primitive.TextureEntry textureEntry); void ObjectSetRotationAxis(object sender, SimulationObject obj, Vector3 rotationAxis); void ObjectApplyImpulse(object sender, SimulationObject obj, Vector3 impulse); void ObjectApplyRotationalImpulse(object sender, SimulationObject obj, Vector3 impulse); diff --git a/Programs/Simian/SimulationObject.cs b/Programs/Simian/SimulationObject.cs index 38b91727..fc530693 100644 --- a/Programs/Simian/SimulationObject.cs +++ b/Programs/Simian/SimulationObject.cs @@ -27,10 +27,20 @@ namespace Simian public Vector3 Torque; /// Last point the object was attached to (right hand by default) public AttachmentPoint LastAttachmentPoint = AttachmentPoint.RightHand; - /// Seat offset + /// Saved seat offset. Applied to avatars that sit on this object public Vector3 SitPosition; - /// Seat rotation + /// Saved seat rotation. Applied to avatars that sit on this object public Quaternion SitRotation = Quaternion.Identity; + /// Saved attachment offset. Applied to this object when it is attached + /// to an avatar + public Vector3 AttachmentPosition; + /// Saved attachment rotation. Applied to this object when it is attached + /// to an avatar + public Quaternion AttachmentRotation = Quaternion.Identity; + /// Rotation that is saved when this object is attached to an avatar. + /// Will be applied to the object when it is dropped. This is always the world + /// rotation, since it is only applicable to parent objects + public Quaternion BeforeAttachmentRotation = Quaternion.Identity; protected Simian Server; protected SimpleMesh[] Meshes; @@ -247,62 +257,74 @@ namespace Simian return update; } - public static ObjectUpdatePacket.ObjectDataBlock BuildUpdateBlock(Primitive obj, PrimFlags flags, uint crc) + public static ObjectUpdatePacket.ObjectDataBlock BuildUpdateBlock(Primitive prim, PrimFlags flags, uint crc) { byte[] objectData = new byte[60]; - obj.Position.ToBytes(objectData, 0); - obj.Velocity.ToBytes(objectData, 12); - obj.Acceleration.ToBytes(objectData, 24); - obj.Rotation.ToBytes(objectData, 36); - obj.AngularVelocity.ToBytes(objectData, 48); + prim.Position.ToBytes(objectData, 0); + prim.Velocity.ToBytes(objectData, 12); + prim.Acceleration.ToBytes(objectData, 24); + prim.Rotation.ToBytes(objectData, 36); + prim.AngularVelocity.ToBytes(objectData, 48); ObjectUpdatePacket.ObjectDataBlock update = new ObjectUpdatePacket.ObjectDataBlock(); - update.ClickAction = (byte)obj.ClickAction; + update.ClickAction = (byte)prim.ClickAction; update.CRC = crc; - update.ExtraParams = obj.GetExtraParamsBytes(); + update.ExtraParams = prim.GetExtraParamsBytes(); update.Flags = (byte)flags; - update.FullID = obj.ID; - update.Gain = obj.SoundGain; - update.ID = obj.LocalID; - update.JointAxisOrAnchor = obj.JointAxisOrAnchor; - update.JointPivot = obj.JointPivot; - update.JointType = (byte)obj.Joint; - update.Material = (byte)obj.PrimData.Material; - update.MediaURL = Utils.StringToBytes(obj.MediaURL); - update.NameValue = Utils.StringToBytes(NameValue.NameValuesToString(obj.NameValues)); + update.FullID = prim.ID; + update.Gain = prim.SoundGain; + update.ID = prim.LocalID; + update.JointAxisOrAnchor = prim.JointAxisOrAnchor; + update.JointPivot = prim.JointPivot; + update.JointType = (byte)prim.Joint; + update.Material = (byte)prim.PrimData.Material; + update.MediaURL = Utils.StringToBytes(prim.MediaURL); + update.NameValue = Utils.StringToBytes(NameValue.NameValuesToString(prim.NameValues)); update.ObjectData = objectData; - update.OwnerID = (obj.Properties != null ? obj.Properties.OwnerID : UUID.Zero); - update.ParentID = obj.ParentID; - update.PathBegin = Primitive.PackBeginCut(obj.PrimData.PathBegin); - update.PathCurve = (byte)obj.PrimData.PathCurve; - update.PathEnd = Primitive.PackEndCut(obj.PrimData.PathEnd); - update.PathRadiusOffset = Primitive.PackPathTwist(obj.PrimData.PathRadiusOffset); - update.PathRevolutions = Primitive.PackPathRevolutions(obj.PrimData.PathRevolutions); - update.PathScaleX = Primitive.PackPathScale(obj.PrimData.PathScaleX); - update.PathScaleY = Primitive.PackPathScale(obj.PrimData.PathScaleY); - update.PathShearX = (byte)Primitive.PackPathShear(obj.PrimData.PathShearX); - update.PathShearY = (byte)Primitive.PackPathShear(obj.PrimData.PathShearY); - update.PathSkew = Primitive.PackPathTwist(obj.PrimData.PathSkew); - update.PathTaperX = Primitive.PackPathTaper(obj.PrimData.PathTaperX); - update.PathTaperY = Primitive.PackPathTaper(obj.PrimData.PathTaperY); - update.PathTwist = Primitive.PackPathTwist(obj.PrimData.PathTwist); - update.PathTwistBegin = Primitive.PackPathTwist(obj.PrimData.PathTwistBegin); - update.PCode = (byte)obj.PrimData.PCode; - update.ProfileBegin = Primitive.PackBeginCut(obj.PrimData.ProfileBegin); - update.ProfileCurve = (byte)obj.PrimData.ProfileCurve; - update.ProfileEnd = Primitive.PackEndCut(obj.PrimData.ProfileEnd); - update.ProfileHollow = Primitive.PackProfileHollow(obj.PrimData.ProfileHollow); - update.PSBlock = obj.ParticleSys.GetBytes(); - update.TextColor = obj.TextColor.GetBytes(true); - update.TextureAnim = obj.TextureAnim.GetBytes(); - update.TextureEntry = obj.Textures == null ? Utils.EmptyBytes : obj.Textures.ToBytes(); - update.Radius = obj.SoundRadius; - update.Scale = obj.Scale; - update.Sound = obj.Sound; - update.State = obj.PrimData.State; - update.Text = Utils.StringToBytes(obj.Text); + update.OwnerID = (prim.Properties != null ? prim.Properties.OwnerID : UUID.Zero); + update.ParentID = prim.ParentID; + update.PathBegin = Primitive.PackBeginCut(prim.PrimData.PathBegin); + update.PathCurve = (byte)prim.PrimData.PathCurve; + update.PathEnd = Primitive.PackEndCut(prim.PrimData.PathEnd); + update.PathRadiusOffset = Primitive.PackPathTwist(prim.PrimData.PathRadiusOffset); + update.PathRevolutions = Primitive.PackPathRevolutions(prim.PrimData.PathRevolutions); + update.PathScaleX = Primitive.PackPathScale(prim.PrimData.PathScaleX); + update.PathScaleY = Primitive.PackPathScale(prim.PrimData.PathScaleY); + update.PathShearX = (byte)Primitive.PackPathShear(prim.PrimData.PathShearX); + update.PathShearY = (byte)Primitive.PackPathShear(prim.PrimData.PathShearY); + update.PathSkew = Primitive.PackPathTwist(prim.PrimData.PathSkew); + update.PathTaperX = Primitive.PackPathTaper(prim.PrimData.PathTaperX); + update.PathTaperY = Primitive.PackPathTaper(prim.PrimData.PathTaperY); + update.PathTwist = Primitive.PackPathTwist(prim.PrimData.PathTwist); + update.PathTwistBegin = Primitive.PackPathTwist(prim.PrimData.PathTwistBegin); + update.PCode = (byte)prim.PrimData.PCode; + update.ProfileBegin = Primitive.PackBeginCut(prim.PrimData.ProfileBegin); + update.ProfileCurve = (byte)prim.PrimData.ProfileCurve; + update.ProfileEnd = Primitive.PackEndCut(prim.PrimData.ProfileEnd); + update.ProfileHollow = Primitive.PackProfileHollow(prim.PrimData.ProfileHollow); + update.PSBlock = prim.ParticleSys.GetBytes(); + update.TextColor = prim.TextColor.GetBytes(true); + update.TextureAnim = prim.TextureAnim.GetBytes(); + update.TextureEntry = prim.Textures == null ? Utils.EmptyBytes : prim.Textures.GetBytes(); + update.Radius = prim.SoundRadius; + update.Scale = prim.Scale; + update.Sound = prim.Sound; + update.State = prim.PrimData.State; + update.Text = Utils.StringToBytes(prim.Text); update.UpdateFlags = (uint)flags; - update.Data = obj.GenericData == null ? Utils.EmptyBytes : obj.GenericData; + switch (prim.PrimData.PCode) + { + case PCode.Grass: + case PCode.Tree: + case PCode.NewTree: + update.Data = new byte[1]; + update.Data[0] = (byte)prim.TreeSpecies; + break; + default: + update.Data = new byte[prim.ScratchPad.Length]; + Buffer.BlockCopy(prim.ScratchPad, 0, update.Data, 0, update.Data.Length); + break; + } return update; }