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; }