diff --git a/OpenMetaverse/Types/Color4.cs b/OpenMetaverse/Types/Color4.cs
index ce4b97ce..5ecc2d8b 100644
--- a/OpenMetaverse/Types/Color4.cs
+++ b/OpenMetaverse/Types/Color4.cs
@@ -85,6 +85,9 @@ namespace OpenMetaverse
///
/// Byte array containing a 16 byte color
/// Beginning position in the byte array
+ /// True if the byte array stores inverted values,
+ /// otherwise false. For example the color black (fully opaque) inverted
+ /// would be 0xFF 0xFF 0xFF 0x00
public Color4(byte[] byteArray, int pos, bool inverted)
{
R = G = B = A = 0f;
diff --git a/OpenMetaverse/Types/Quaternion.cs b/OpenMetaverse/Types/Quaternion.cs
index a6808de4..a52a2869 100644
--- a/OpenMetaverse/Types/Quaternion.cs
+++ b/OpenMetaverse/Types/Quaternion.cs
@@ -373,7 +373,7 @@ namespace OpenMetaverse
/// Creates a quaternion from a vector containing roll, pitch, and yaw
/// in radians
///
- /// Vector representation of the euler angles in
+ /// Vector representation of the euler angles in
/// radians
/// Quaternion representation of the euler angles
public static Quaternion CreateFromEulers(Vector3 eulers)
diff --git a/Programs/Simian/Agent.cs b/Programs/Simian/Agent.cs
index 4c1227b3..a8aa24d7 100644
--- a/Programs/Simian/Agent.cs
+++ b/Programs/Simian/Agent.cs
@@ -19,6 +19,7 @@ namespace Simian
public int Balance;
public bool Running;
public AgentManager.ControlFlags ControlFlags = AgentManager.ControlFlags.NONE;
+ public List Animations = new List();
public Dictionary Inventory = new Dictionary();
public Dictionary Library = new Dictionary();
public Dictionary Wearables = new Dictionary();
@@ -59,6 +60,13 @@ namespace Simian
Settings.NETWORK_TICK_INTERVAL);
}
+ public void Dispose()
+ {
+ ackTimer.Dispose();
+ packetArchive.Clear();
+ needAcks.Clear();
+ }
+
public void SendPacket(Packet packet)
{
SendPacket(packet, true);
diff --git a/Programs/Simian/Extensions/ConnectionManagement.cs b/Programs/Simian/Extensions/ConnectionManagement.cs
index 32621611..3c8bc4dc 100644
--- a/Programs/Simian/Extensions/ConnectionManagement.cs
+++ b/Programs/Simian/Extensions/ConnectionManagement.cs
@@ -88,6 +88,7 @@ namespace Simian.Extensions
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
kill.ObjectData[0].ID = agent.Avatar.LocalID;
+ agent.Dispose();
server.Agents.Remove(agent.Address);
foreach (Agent recipient in server.Agents.Values)
diff --git a/Programs/Simian/Extensions/Messaging.cs b/Programs/Simian/Extensions/Messaging.cs
new file mode 100644
index 00000000..d03f09cd
--- /dev/null
+++ b/Programs/Simian/Extensions/Messaging.cs
@@ -0,0 +1,90 @@
+using OpenMetaverse;
+using OpenMetaverse.Packets;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading;
+
+namespace Simian.Extensions
+{
+ public class Messaging : ISimianExtension
+ {
+ Simian Server;
+
+ public Messaging(Simian server)
+ {
+ Server = server;
+ }
+
+ public void Start()
+ {
+ Server.UDPServer.RegisterPacketCallback(PacketType.ChatFromViewer, new UDPServer.PacketCallback(ChatFromViewerHandler));
+ Server.UDPServer.RegisterPacketCallback(PacketType.ImprovedInstantMessage, new UDPServer.PacketCallback(ImprovedInstantMessageHandler));
+ }
+
+ public void Stop()
+ {
+ }
+
+ void ChatFromViewerHandler(Packet packet, Agent agent)
+ {
+ ChatFromViewerPacket viewerChat = (ChatFromViewerPacket)packet;
+
+ if (viewerChat.ChatData.Channel != 0) return; //not public chat
+
+ //TODO: add distance constraints to AudibleLevel and Message
+
+ ChatFromSimulatorPacket chat = new ChatFromSimulatorPacket();
+ chat.ChatData.Audible = (byte)ChatAudibleLevel.Fully;
+ chat.ChatData.ChatType = viewerChat.ChatData.Type;
+ chat.ChatData.OwnerID = agent.AgentID;
+ chat.ChatData.SourceID = agent.AgentID;
+ chat.ChatData.SourceType = (byte)ChatSourceType.Agent;
+ chat.ChatData.Position = agent.Avatar.Position;
+ chat.ChatData.FromName = Utils.StringToBytes(agent.Avatar.Name);
+ chat.ChatData.Message = viewerChat.ChatData.Message;
+
+ lock (Server.Agents)
+ {
+ foreach(Agent recipient in Server.Agents.Values)
+ recipient.SendPacket(chat);
+ }
+ }
+
+ void ImprovedInstantMessageHandler(Packet packet, Agent agent)
+ {
+ ImprovedInstantMessagePacket im = (ImprovedInstantMessagePacket)packet;
+ InstantMessageDialog dialog = (InstantMessageDialog)im.MessageBlock.Dialog;
+
+ if (dialog == InstantMessageDialog.MessageFromAgent)
+ {
+ lock (Server.Agents)
+ {
+ foreach (Agent recipient in Server.Agents.Values)
+ {
+ if (recipient.AgentID == im.MessageBlock.ToAgentID)
+ {
+ ImprovedInstantMessagePacket sendIM = new ImprovedInstantMessagePacket();
+ sendIM.MessageBlock.RegionID = UUID.Random(); //FIXME
+ sendIM.MessageBlock.ParentEstateID = 1;
+ sendIM.MessageBlock.FromGroup = false;
+ sendIM.MessageBlock.FromAgentName = Utils.StringToBytes(agent.Avatar.Name);
+ sendIM.MessageBlock.ToAgentID = im.MessageBlock.ToAgentID;
+ sendIM.MessageBlock.Dialog = (byte)InstantMessageDialog.MessageFromAgent;
+ sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online;
+ sendIM.MessageBlock.ID = agent.AgentID;
+ sendIM.MessageBlock.Message = im.MessageBlock.Message;
+ sendIM.MessageBlock.BinaryBucket = new byte[0];
+ sendIM.MessageBlock.Timestamp = 0;
+ sendIM.MessageBlock.Position = agent.Avatar.Position;
+ recipient.SendPacket(sendIM);
+
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/Programs/Simian/Extensions/Movement.cs b/Programs/Simian/Extensions/Movement.cs
index 0c80099e..d3d90e4d 100644
--- a/Programs/Simian/Extensions/Movement.cs
+++ b/Programs/Simian/Extensions/Movement.cs
@@ -11,10 +11,21 @@ namespace Simian.Extensions
{
Simian Server;
Timer UpdateTimer;
+ long _LastTick;
+
+ const int UPDATE_ITERATION = 100;
+
+ const float WALK_SPEED = 3f;
+ const float RUN_SPEED = 6f;
+ const float FLY_SPEED = 12f;
+
const float SQRT_TWO = 1.41421356f;
- const float WALK_SPEED = 0.5f;
- const float RUN_SPEED = 1.0f;
- const float FLY_SPEED = 1.5f;
+
+ public int LastTick
+ {
+ get { return (int) Interlocked.Read(ref _LastTick); }
+ set { Interlocked.Exchange(ref _LastTick, value); }
+ }
public Movement(Simian server)
{
@@ -23,13 +34,15 @@ namespace Simian.Extensions
public void Start()
{
+ Server.UDPServer.RegisterPacketCallback(PacketType.AgentAnimation, new UDPServer.PacketCallback(AgentAnimationHandler));
Server.UDPServer.RegisterPacketCallback(PacketType.AgentUpdate, new UDPServer.PacketCallback(AgentUpdateHandler));
Server.UDPServer.RegisterPacketCallback(PacketType.AgentHeightWidth, new UDPServer.PacketCallback(AgentHeightWidthHandler));
Server.UDPServer.RegisterPacketCallback(PacketType.SetAlwaysRun, new UDPServer.PacketCallback(SetAlwaysRunHandler));
Server.UDPServer.RegisterPacketCallback(PacketType.ViewerEffect, new UDPServer.PacketCallback(ViewerEffectHandler));
UpdateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
- UpdateTimer.Change(100, 100);
+ LastTick = Environment.TickCount;
+ UpdateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
}
public void Stop()
@@ -39,6 +52,10 @@ namespace Simian.Extensions
void UpdateTimer_Elapsed(object sender)
{
+ int tick = Environment.TickCount;
+ float seconds = (float)((tick - LastTick) / 1000f);
+ LastTick = tick;
+
lock (Server.Agents)
{
foreach (Agent agent in Server.Agents.Values)
@@ -53,10 +70,15 @@ namespace Simian.Extensions
bool heldBack = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_AT_NEG;
bool heldLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_POS;
bool heldRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_LEFT_NEG;
+ bool heldTurnLeft = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_LEFT;
+ bool heldTurnRight = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT) == AgentManager.ControlFlags.AGENT_CONTROL_TURN_RIGHT;
bool heldUp = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_POS) == AgentManager.ControlFlags.AGENT_CONTROL_UP_POS;
bool heldDown = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG) == AgentManager.ControlFlags.AGENT_CONTROL_UP_NEG;
bool flying = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_FLY) == AgentManager.ControlFlags.AGENT_CONTROL_FLY;
-
+ bool mouselook = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) == AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK;
+
+ float speed = seconds * (flying ? FLY_SPEED : agent.Running ? RUN_SPEED : WALK_SPEED);
+
Vector3 move = Vector3.Zero;
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
@@ -68,10 +90,11 @@ namespace Simian.Extensions
float newFloor = GetLandHeightAt(agent.Avatar.Position + move);
float lowerLimit = newFloor + agent.Avatar.Scale.Z / 2;
- float speed = flying ? FLY_SPEED : agent.Running ? RUN_SPEED : WALK_SPEED;
if ((heldForward || heldBack) && (heldLeft || heldRight))
speed /= SQRT_TWO;
+ if (!flying && newFloor != oldFloor) speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
+
if (flying)
{
if (heldUp)
@@ -102,20 +125,34 @@ namespace Simian.Extensions
{
AgentUpdatePacket update = (AgentUpdatePacket)packet;
+ agent.Avatar.Rotation = update.AgentData.BodyRotation;
+ agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
+ agent.State = update.AgentData.State;
+ agent.Flags = (LLObject.ObjectFlags)update.AgentData.Flags;
+
lock (Server.Agents)
{
- agent.Avatar.Rotation = update.AgentData.BodyRotation;
- agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
- agent.State = update.AgentData.State;
- //agent.Flags = (LLObject.ObjectFlags)update.AgentData.Flags;
-
ObjectUpdatePacket fullUpdate = BuildFullUpdate(agent, agent.Avatar, Server.RegionHandle,
agent.State, agent.Flags);
- lock (Server.Agents)
+ foreach (Agent recipient in Server.Agents.Values)
{
- foreach (Agent recipient in Server.Agents.Values)
- recipient.SendPacket(fullUpdate);
+ recipient.SendPacket(fullUpdate);
+
+ if (agent.Animations.Count == 0) //TODO: need to start default standing animation
+ {
+ agent.Animations.Add(ANIM_STAND);
+
+ AgentAnimationPacket startAnim = new AgentAnimationPacket();
+ startAnim.AgentData.AgentID = agent.AgentID;
+ startAnim.AnimationList = new AgentAnimationPacket.AnimationListBlock[1];
+ startAnim.AnimationList[0] = new AgentAnimationPacket.AnimationListBlock();
+ startAnim.AnimationList[0].AnimID = ANIM_STAND;
+ startAnim.AnimationList[0].StartAnim = true;
+ startAnim.PhysicalAvatarEventList = new AgentAnimationPacket.PhysicalAvatarEventListBlock[0];
+
+ recipient.SendPacket(startAnim);
+ }
}
}
}
@@ -156,6 +193,30 @@ namespace Simian.Extensions
return ((lerpX + lerpY) / 2);
}
+ void AgentAnimationHandler(Packet packet, Agent agent)
+ {
+ AgentAnimationPacket anim = (AgentAnimationPacket)packet;
+ anim.AgentData.SessionID = UUID.Zero;
+
+ lock (agent.Animations)
+ {
+ foreach (AgentAnimationPacket.AnimationListBlock block in anim.AnimationList)
+ {
+ if (agent.Animations.Contains(block.AnimID))
+ {
+ if (!block.StartAnim) agent.Animations.Remove(block.AnimID);
+ }
+ else if (block.StartAnim) agent.Animations.Add(block.AnimID);
+ }
+ }
+
+ lock (Server.Agents)
+ {
+ foreach (Agent recipient in Server.Agents.Values)
+ recipient.SendPacket(anim);
+ }
+ }
+
void AgentHeightWidthHandler(Packet packet, Agent agent)
{
AgentHeightWidthPacket heightWidth = (AgentHeightWidthPacket)packet;