* Moved TexturePipeline into libOpenMetaverse (still needs to be instantiated separately)
* Moved TextureCache into its own file * New version of Periscope, major improvements. Check the Simian.ini file for usage, and change the const values in Periscope.cs * Lots of miscellaneous cleanups and improvements in Simian git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2383 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
@@ -62,6 +62,12 @@ namespace Simian.Extensions
|
||||
return agent.Animations.Remove(animID);
|
||||
}
|
||||
|
||||
public bool ClearAnimations(Agent agent)
|
||||
{
|
||||
agent.Animations.Clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void SendAnimations(Agent agent)
|
||||
{
|
||||
AvatarAnimationPacket sendAnim = new AvatarAnimationPacket();
|
||||
@@ -104,7 +110,7 @@ namespace Simian.Extensions
|
||||
// Remove the avatar from the scene
|
||||
SimulationObject obj;
|
||||
if (server.Scene.TryGetObject(agent.AgentID, out obj))
|
||||
server.Scene.ObjectRemove(this, obj);
|
||||
server.Scene.ObjectRemove(this, obj.Prim.LocalID);
|
||||
else
|
||||
Logger.Log("Disconnecting an agent that is not in the scene", Helpers.LogLevel.Warning);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Imaging;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
@@ -14,14 +13,16 @@ namespace Simian.Extensions
|
||||
public const int IMAGE_PACKET_SIZE = 1000;
|
||||
|
||||
public AssetTexture Texture;
|
||||
public Agent Agent;
|
||||
public int DiscardLevel;
|
||||
public float Priority;
|
||||
public int CurrentPacket;
|
||||
public int StopPacket;
|
||||
|
||||
public ImageDownload(AssetTexture texture, int discardLevel, float priority, int packet)
|
||||
public ImageDownload(AssetTexture texture, Agent agent, int discardLevel, float priority, int packet)
|
||||
{
|
||||
Texture = texture;
|
||||
Agent = agent;
|
||||
Update(discardLevel, priority, packet);
|
||||
}
|
||||
|
||||
@@ -35,9 +36,28 @@ namespace Simian.Extensions
|
||||
public void Update(int discardLevel, float priority, int packet)
|
||||
{
|
||||
Priority = priority;
|
||||
DiscardLevel = Utils.Clamp(discardLevel, 0, Texture.LayerInfo.Length - 1);
|
||||
StopPacket = GetPacketForBytePosition(Texture.LayerInfo[(Texture.LayerInfo.Length - 1) - DiscardLevel].End);
|
||||
CurrentPacket = Utils.Clamp(packet, 1, TexturePacketCount());
|
||||
|
||||
if (Texture != null)
|
||||
{
|
||||
if (Texture.LayerInfo != null && Texture.LayerInfo.Length > 0)
|
||||
{
|
||||
DiscardLevel = Utils.Clamp(discardLevel, 0, Texture.LayerInfo.Length - 1);
|
||||
StopPacket = GetPacketForBytePosition(Texture.LayerInfo[(Texture.LayerInfo.Length - 1) - DiscardLevel].End);
|
||||
}
|
||||
else
|
||||
{
|
||||
DiscardLevel = 0;
|
||||
StopPacket = GetPacketForBytePosition(Texture.AssetData.Length);
|
||||
}
|
||||
|
||||
CurrentPacket = Utils.Clamp(packet, 1, TexturePacketCount());
|
||||
}
|
||||
else
|
||||
{
|
||||
DiscardLevel = discardLevel;
|
||||
Priority = priority;
|
||||
CurrentPacket = packet;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -94,7 +114,7 @@ namespace Simian.Extensions
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.RequestImage, new PacketCallback(RequestImageHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.RequestImage, RequestImageHandler);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@@ -151,91 +171,7 @@ namespace Simian.Extensions
|
||||
Asset asset;
|
||||
if (server.Assets.TryGetAsset(block.Image, out asset) && asset is AssetTexture)
|
||||
{
|
||||
download = new ImageDownload((AssetTexture)asset, block.DiscardLevel, block.DownloadPriority,
|
||||
(int)block.Packet);
|
||||
|
||||
Logger.DebugLog(String.Format(
|
||||
"Starting new download for {0}, DiscardLevel: {1}, Priority: {2}, Start: {3}, End: {4}, Total: {5}",
|
||||
block.Image, block.DiscardLevel, block.DownloadPriority, download.CurrentPacket, download.StopPacket,
|
||||
download.TexturePacketCount()));
|
||||
|
||||
// Send initial data
|
||||
ImageDataPacket data = new ImageDataPacket();
|
||||
data.ImageID.Codec = (byte)ImageCodec.J2C;
|
||||
data.ImageID.ID = download.Texture.AssetID;
|
||||
data.ImageID.Packets = (ushort)download.TexturePacketCount();
|
||||
data.ImageID.Size = (uint)download.Texture.AssetData.Length;
|
||||
|
||||
// The first bytes of the image are always sent in the ImageData packet
|
||||
data.ImageData = new ImageDataPacket.ImageDataBlock();
|
||||
int imageDataSize = (download.Texture.AssetData.Length >= ImageDownload.FIRST_IMAGE_PACKET_SIZE) ?
|
||||
ImageDownload.FIRST_IMAGE_PACKET_SIZE : download.Texture.AssetData.Length;
|
||||
try
|
||||
{
|
||||
data.ImageData.Data = new byte[imageDataSize];
|
||||
Buffer.BlockCopy(download.Texture.AssetData, 0, data.ImageData.Data, 0, imageDataSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format("{0}: imageDataSize={1}", ex.Message, imageDataSize),
|
||||
Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, data, PacketCategory.Texture);
|
||||
|
||||
// Check if ImagePacket packets need to be sent to complete this transfer
|
||||
if (download.CurrentPacket <= download.StopPacket)
|
||||
{
|
||||
// Insert this download into the dictionary
|
||||
lock (CurrentDownloads)
|
||||
CurrentDownloads[block.Image] = download;
|
||||
|
||||
// Send all of the remaining packets
|
||||
ThreadPool.QueueUserWorkItem(
|
||||
delegate(object obj)
|
||||
{
|
||||
while (download.CurrentPacket <= download.StopPacket)
|
||||
{
|
||||
if (download.Priority == 0.0f && download.DiscardLevel == -1)
|
||||
break;
|
||||
|
||||
lock (download)
|
||||
{
|
||||
int imagePacketSize = (download.CurrentPacket == download.TexturePacketCount() - 1) ?
|
||||
download.LastPacketSize() : ImageDownload.IMAGE_PACKET_SIZE;
|
||||
|
||||
ImagePacketPacket transfer = new ImagePacketPacket();
|
||||
transfer.ImageID.ID = block.Image;
|
||||
transfer.ImageID.Packet = (ushort)download.CurrentPacket;
|
||||
transfer.ImageData.Data = new byte[imagePacketSize];
|
||||
|
||||
try
|
||||
{
|
||||
Buffer.BlockCopy(download.Texture.AssetData, download.CurrentBytePosition(),
|
||||
transfer.ImageData.Data, 0, imagePacketSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"{0}: CurrentBytePosition()={1}, AssetData.Length={2} imagePacketSize={3}",
|
||||
ex.Message, download.CurrentBytePosition(), download.Texture.AssetData.Length,
|
||||
imagePacketSize), Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, transfer, PacketCategory.Texture);
|
||||
|
||||
++download.CurrentPacket;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.DebugLog("Completed image transfer for " + block.Image.ToString());
|
||||
|
||||
// Transfer is complete, remove the reference
|
||||
lock (CurrentDownloads)
|
||||
CurrentDownloads.Remove(block.Image);
|
||||
}
|
||||
);
|
||||
}
|
||||
SendTexture(agent, (AssetTexture)asset, block.DiscardLevel, (int)block.Packet, block.DownloadPriority);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -248,5 +184,93 @@ namespace Simian.Extensions
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SendTexture(Agent agent, AssetTexture texture, int discardLevel, int packet, float priority)
|
||||
{
|
||||
ImageDownload download = new ImageDownload(texture, agent, discardLevel, priority, packet);
|
||||
|
||||
Logger.DebugLog(String.Format(
|
||||
"Starting new download for {0}, DiscardLevel: {1}, Priority: {2}, Start: {3}, End: {4}, Total: {5}",
|
||||
texture.AssetID, discardLevel, priority, download.CurrentPacket, download.StopPacket,
|
||||
download.TexturePacketCount()));
|
||||
|
||||
// Send initial data
|
||||
ImageDataPacket data = new ImageDataPacket();
|
||||
data.ImageID.Codec = (byte)ImageCodec.J2C;
|
||||
data.ImageID.ID = download.Texture.AssetID;
|
||||
data.ImageID.Packets = (ushort)download.TexturePacketCount();
|
||||
data.ImageID.Size = (uint)download.Texture.AssetData.Length;
|
||||
|
||||
// The first bytes of the image are always sent in the ImageData packet
|
||||
data.ImageData = new ImageDataPacket.ImageDataBlock();
|
||||
int imageDataSize = (download.Texture.AssetData.Length >= ImageDownload.FIRST_IMAGE_PACKET_SIZE) ?
|
||||
ImageDownload.FIRST_IMAGE_PACKET_SIZE : download.Texture.AssetData.Length;
|
||||
try
|
||||
{
|
||||
data.ImageData.Data = new byte[imageDataSize];
|
||||
Buffer.BlockCopy(download.Texture.AssetData, 0, data.ImageData.Data, 0, imageDataSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format("{0}: imageDataSize={1}", ex.Message, imageDataSize),
|
||||
Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, data, PacketCategory.Texture);
|
||||
|
||||
// Check if ImagePacket packets need to be sent to complete this transfer
|
||||
if (download.CurrentPacket <= download.StopPacket)
|
||||
{
|
||||
// Insert this download into the dictionary
|
||||
lock (CurrentDownloads)
|
||||
CurrentDownloads[texture.AssetID] = download;
|
||||
|
||||
// Send all of the remaining packets
|
||||
ThreadPool.QueueUserWorkItem(
|
||||
delegate(object obj)
|
||||
{
|
||||
while (download.CurrentPacket <= download.StopPacket)
|
||||
{
|
||||
if (download.Priority == 0.0f && download.DiscardLevel == -1)
|
||||
break;
|
||||
|
||||
lock (download)
|
||||
{
|
||||
int imagePacketSize = (download.CurrentPacket == download.TexturePacketCount() - 1) ?
|
||||
download.LastPacketSize() : ImageDownload.IMAGE_PACKET_SIZE;
|
||||
|
||||
ImagePacketPacket transfer = new ImagePacketPacket();
|
||||
transfer.ImageID.ID = texture.AssetID;
|
||||
transfer.ImageID.Packet = (ushort)download.CurrentPacket;
|
||||
transfer.ImageData.Data = new byte[imagePacketSize];
|
||||
|
||||
try
|
||||
{
|
||||
Buffer.BlockCopy(download.Texture.AssetData, download.CurrentBytePosition(),
|
||||
transfer.ImageData.Data, 0, imagePacketSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"{0}: CurrentBytePosition()={1}, AssetData.Length={2} imagePacketSize={3}",
|
||||
ex.Message, download.CurrentBytePosition(), download.Texture.AssetData.Length,
|
||||
imagePacketSize), Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, transfer, PacketCategory.Texture);
|
||||
|
||||
++download.CurrentPacket;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.DebugLog("Completed image transfer for " + texture.AssetID.ToString());
|
||||
|
||||
// Transfer is complete, remove the reference
|
||||
lock (CurrentDownloads)
|
||||
CurrentDownloads.Remove(texture.AssetID);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,7 +310,6 @@ namespace Simian.Extensions
|
||||
if (animsChanged)
|
||||
server.Avatars.SendAnimations(agent);
|
||||
|
||||
|
||||
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
|
||||
|
||||
// static acceleration when any control is held, otherwise none
|
||||
@@ -339,7 +338,6 @@ namespace Simian.Extensions
|
||||
else if (agent.Avatar.Position.Y > 255) agent.Avatar.Position.Y = 255f;
|
||||
|
||||
if (agent.Avatar.Position.Z < lowerLimit) agent.Avatar.Position.Z = lowerLimit;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -403,6 +401,5 @@ namespace Simian.Extensions
|
||||
//Logger.Log(String.Format("Agent wants to set height={0}, width={1}",
|
||||
// heightWidth.HeightWidthBlock.Height, heightWidth.HeightWidthBlock.Width), Helpers.LogLevel.Info);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -440,7 +440,7 @@ namespace Simian.Extensions
|
||||
data.ProfileEnd = Primitive.UnpackEndCut(block.ProfileEnd);
|
||||
data.ProfileHollow = Primitive.UnpackProfileHollow(block.ProfileHollow);
|
||||
|
||||
server.Scene.ObjectModify(this, obj, data);
|
||||
server.Scene.ObjectModify(this, obj.Prim.LocalID, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -569,7 +569,7 @@ namespace Simian.Extensions
|
||||
server.Inventory.CreateItem(agent.AgentID, obj.Prim.Properties.Name, obj.Prim.Properties.Description, InventoryType.Object,
|
||||
AssetType.Object, obj.Prim.ID, trash.ID, PermissionMask.All, PermissionMask.All, agent.AgentID,
|
||||
obj.Prim.Properties.CreatorID, derez.AgentBlock.TransactionID, 0, true);
|
||||
server.Scene.ObjectRemove(this, obj);
|
||||
server.Scene.ObjectRemove(this, obj.Prim.LocalID);
|
||||
|
||||
Logger.DebugLog(String.Format("Derezzed prim {0} to agent inventory trash", obj.Prim.LocalID));
|
||||
}
|
||||
@@ -606,6 +606,7 @@ namespace Simian.Extensions
|
||||
|
||||
for (int i = 0; i < update.ObjectData.Length; i++)
|
||||
{
|
||||
bool scaled = false;
|
||||
MultipleObjectUpdatePacket.ObjectDataBlock block = update.ObjectData[i];
|
||||
|
||||
SimulationObject obj;
|
||||
@@ -630,6 +631,7 @@ namespace Simian.Extensions
|
||||
}
|
||||
if ((type & UpdateType.Scale) != 0)
|
||||
{
|
||||
scaled = true;
|
||||
scale = new Vector3(block.Data, pos);
|
||||
pos += 12;
|
||||
|
||||
@@ -637,12 +639,19 @@ namespace Simian.Extensions
|
||||
bool uniform = ((type & UpdateType.Uniform) != 0);
|
||||
}
|
||||
|
||||
// Although the object has already been modified, we need
|
||||
// to inform the scene manager of the changes so they are
|
||||
// sent to clients and propagated to other extensions
|
||||
server.Scene.ObjectTransform(this, obj, position, rotation,
|
||||
obj.Prim.Velocity, obj.Prim.Acceleration, obj.Prim.AngularVelocity,
|
||||
scale);
|
||||
if (scaled)
|
||||
{
|
||||
obj.Prim.Position = position;
|
||||
obj.Prim.Rotation = rotation;
|
||||
obj.Prim.Scale = scale;
|
||||
|
||||
server.Scene.ObjectAdd(this, obj, PrimFlags.None);
|
||||
}
|
||||
else
|
||||
{
|
||||
server.Scene.ObjectTransform(this, obj.Prim.LocalID, position, rotation,
|
||||
obj.Prim.Velocity, obj.Prim.Acceleration, obj.Prim.AngularVelocity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -12,45 +12,54 @@ namespace Simian.Extensions
|
||||
const string FIRST_NAME = "Testing";
|
||||
const string LAST_NAME = "Anvil";
|
||||
const string PASSWORD = "testinganvil";
|
||||
const string SIMULATOR = "Svarga";
|
||||
|
||||
public Agent MasterAgent = null;
|
||||
|
||||
Simian server;
|
||||
GridClient client;
|
||||
PeriscopeImageDelivery imageDelivery;
|
||||
PeriscopeMovement movement;
|
||||
object loginLock = new object();
|
||||
|
||||
public Periscope()
|
||||
{
|
||||
client = new GridClient();
|
||||
client.Settings.SEND_AGENT_UPDATES = false;
|
||||
|
||||
client.Objects.OnNewPrim += new OpenMetaverse.ObjectManager.NewPrimCallback(Objects_OnNewPrim);
|
||||
client.Terrain.OnLandPatch += new TerrainManager.LandPatchCallback(Terrain_OnLandPatch);
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
client = new GridClient();
|
||||
Settings.LOG_LEVEL = Helpers.LogLevel.Info;
|
||||
client.Settings.MULTIPLE_SIMS = false;
|
||||
client.Settings.SEND_AGENT_UPDATES = false;
|
||||
|
||||
client.Network.OnCurrentSimChanged += new NetworkManager.CurrentSimChangedCallback(Network_OnCurrentSimChanged);
|
||||
client.Objects.OnNewPrim += new OpenMetaverse.ObjectManager.NewPrimCallback(Objects_OnNewPrim);
|
||||
client.Objects.OnNewFoliage += new OpenMetaverse.ObjectManager.NewFoliageCallback(Objects_OnNewFoliage);
|
||||
client.Objects.OnNewAvatar += new OpenMetaverse.ObjectManager.NewAvatarCallback(Objects_OnNewAvatar);
|
||||
client.Objects.OnNewAttachment += new OpenMetaverse.ObjectManager.NewAttachmentCallback(Objects_OnNewAttachment);
|
||||
client.Objects.OnObjectKilled += new OpenMetaverse.ObjectManager.KillObjectCallback(Objects_OnObjectKilled);
|
||||
client.Objects.OnObjectUpdated += new OpenMetaverse.ObjectManager.ObjectUpdatedCallback(Objects_OnObjectUpdated);
|
||||
client.Avatars.OnAvatarAppearance += new OpenMetaverse.AvatarManager.AvatarAppearanceCallback(Avatars_OnAvatarAppearance);
|
||||
client.Terrain.OnLandPatch += new TerrainManager.LandPatchCallback(Terrain_OnLandPatch);
|
||||
client.Self.OnChat += new AgentManager.ChatCallback(Self_OnChat);
|
||||
client.Network.RegisterCallback(PacketType.AvatarAnimation, AvatarAnimationHandler);
|
||||
client.Network.RegisterCallback(PacketType.RegionHandshake, RegionHandshakeHandler);
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ChatFromViewer, ChatFromViewerHandler);
|
||||
|
||||
// Start the login process
|
||||
Thread loginThread = new Thread(new ThreadStart(
|
||||
delegate()
|
||||
{
|
||||
client.Network.Login(FIRST_NAME, LAST_NAME, PASSWORD, "Simian Periscope", "1.0.0");
|
||||
|
||||
if (client.Network.Connected)
|
||||
{
|
||||
Logger.Log("Periscope is connected: " + client.Network.LoginMessage, Helpers.LogLevel.Info);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Periscope failed to connect to the foreign grid: " + client.Network.LoginErrorKey, Helpers.LogLevel.Error);
|
||||
}
|
||||
}
|
||||
));
|
||||
loginThread.Start();
|
||||
imageDelivery = new PeriscopeImageDelivery(server, client);
|
||||
movement = new PeriscopeMovement(server, this);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
movement.Stop();
|
||||
imageDelivery.Stop();
|
||||
|
||||
if (client.Network.Connected)
|
||||
client.Network.Logout();
|
||||
}
|
||||
@@ -58,22 +67,211 @@ namespace Simian.Extensions
|
||||
void Objects_OnNewPrim(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
SimulationObject simObj = new SimulationObject(prim, server);
|
||||
server.Scene.ObjectAdd(this, simObj, prim.Flags);
|
||||
server.Scene.ObjectAdd(this, simObj, PrimFlags.None);
|
||||
}
|
||||
|
||||
void Objects_OnNewAttachment(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
SimulationObject simObj = new SimulationObject(prim, server);
|
||||
server.Scene.ObjectAdd(this, simObj, PrimFlags.None);
|
||||
}
|
||||
|
||||
void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
// Add the avatar to both the agents list and the scene objects
|
||||
Agent agent = new Agent();
|
||||
agent.AgentID = avatar.ID;
|
||||
agent.Avatar = avatar;
|
||||
agent.CurrentRegionHandle = server.RegionHandle;
|
||||
agent.FirstName = avatar.FirstName;
|
||||
agent.LastName = avatar.LastName;
|
||||
|
||||
lock (server.Agents)
|
||||
server.Agents[agent.AgentID] = agent;
|
||||
|
||||
SimulationObject simObj = new SimulationObject(avatar, server);
|
||||
server.Scene.ObjectAdd(this, simObj, avatar.Flags);
|
||||
}
|
||||
|
||||
void Objects_OnNewFoliage(Simulator simulator, Primitive foliage, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
SimulationObject simObj = new SimulationObject(foliage, server);
|
||||
server.Scene.ObjectAdd(this, simObj, foliage.Flags);
|
||||
}
|
||||
|
||||
void Objects_OnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
server.Scene.ObjectTransform(this, update.LocalID, update.Position, update.Rotation, update.Velocity,
|
||||
update.Acceleration, update.AngularVelocity);
|
||||
|
||||
if (update.LocalID == client.Self.LocalID)
|
||||
{
|
||||
MasterAgent.Avatar.Acceleration = update.Acceleration;
|
||||
MasterAgent.Avatar.AngularVelocity = update.AngularVelocity;
|
||||
MasterAgent.Avatar.CollisionPlane = update.CollisionPlane;
|
||||
MasterAgent.Avatar.Position = update.Position;
|
||||
MasterAgent.Avatar.Rotation = update.Rotation;
|
||||
MasterAgent.Avatar.Velocity = update.Velocity;
|
||||
|
||||
if (update.Textures != null)
|
||||
MasterAgent.Avatar.Textures = update.Textures;
|
||||
}
|
||||
}
|
||||
|
||||
void Objects_OnObjectKilled(Simulator simulator, uint objectID)
|
||||
{
|
||||
server.Scene.ObjectRemove(this, objectID);
|
||||
}
|
||||
|
||||
void Avatars_OnAvatarAppearance(UUID avatarID, bool isTrial, Primitive.TextureEntryFace defaultTexture,
|
||||
Primitive.TextureEntryFace[] faceTextures, List<byte> visualParams)
|
||||
{
|
||||
Agent agent;
|
||||
if (server.Agents.TryGetValue(avatarID, out agent))
|
||||
{
|
||||
Primitive.TextureEntry te = new Primitive.TextureEntry(defaultTexture);
|
||||
te.FaceTextures = faceTextures;
|
||||
|
||||
byte[] vp = (visualParams != null && visualParams.Count > 1 ? visualParams.ToArray() : null);
|
||||
|
||||
Logger.Log("[Periscope] Updating foreign avatar appearance for " + avatarID.ToString(), Helpers.LogLevel.Info);
|
||||
|
||||
|
||||
server.Scene.AvatarAppearance(this, agent, te, vp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("[Periscope] Received a foreign avatar appearance for an unknown avatar", Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void Terrain_OnLandPatch(Simulator simulator, int x, int y, int width, float[] data)
|
||||
{
|
||||
//throw new NotImplementedException();
|
||||
// TODO: When Simian gets a terrain editing interface, switch this over to
|
||||
// edit the scene heightmap instead of sending packets direct to clients
|
||||
int[] patches = new int[1];
|
||||
patches[0] = (y * 16) + x;
|
||||
LayerDataPacket layer = TerrainCompressor.CreateLandPacket(data, x, y);
|
||||
server.UDP.BroadcastPacket(layer, PacketCategory.Terrain);
|
||||
}
|
||||
|
||||
void Self_OnChat(string message, ChatAudibleLevel audible, ChatType type, ChatSourceType sourceType,
|
||||
string fromName, UUID id, UUID ownerid, Vector3 position)
|
||||
{
|
||||
// TODO: Inject chat into the Scene instead of relaying it
|
||||
ChatFromSimulatorPacket chat = new ChatFromSimulatorPacket();
|
||||
chat.ChatData.Audible = (byte)ChatAudibleLevel.Fully;
|
||||
chat.ChatData.ChatType = (byte)type;
|
||||
chat.ChatData.OwnerID = ownerid;
|
||||
chat.ChatData.SourceID = id;
|
||||
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.Transaction);
|
||||
}
|
||||
|
||||
void Network_OnCurrentSimChanged(Simulator PreviousSimulator)
|
||||
{
|
||||
Logger.Log("[Periscope] Sending bot appearance", Helpers.LogLevel.Info);
|
||||
client.Appearance.SetPreviousAppearance(false);
|
||||
}
|
||||
|
||||
void AvatarAnimationHandler(Packet packet, Simulator simulator)
|
||||
{
|
||||
AvatarAnimationPacket animations = (AvatarAnimationPacket)packet;
|
||||
|
||||
Agent agent;
|
||||
if (server.Agents.TryGetValue(animations.Sender.ID, out agent))
|
||||
{
|
||||
agent.Animations.Clear();
|
||||
|
||||
for (int i = 0; i < animations.AnimationList.Length; i++)
|
||||
{
|
||||
AvatarAnimationPacket.AnimationListBlock block = animations.AnimationList[i];
|
||||
agent.Animations.Add(block.AnimID, block.AnimSequenceID);
|
||||
}
|
||||
|
||||
server.Avatars.SendAnimations(agent);
|
||||
}
|
||||
|
||||
if (animations.Sender.ID == client.Self.AgentID)
|
||||
{
|
||||
MasterAgent.Animations.Clear();
|
||||
|
||||
for (int i = 0; i < animations.AnimationList.Length; i++)
|
||||
{
|
||||
AvatarAnimationPacket.AnimationListBlock block = animations.AnimationList[i];
|
||||
MasterAgent.Animations.Add(block.AnimID, block.AnimSequenceID);
|
||||
}
|
||||
|
||||
server.Avatars.SendAnimations(MasterAgent);
|
||||
}
|
||||
}
|
||||
|
||||
void RegionHandshakeHandler(Packet packet, Simulator simulator)
|
||||
{
|
||||
RegionHandshakePacket handshake = (RegionHandshakePacket)packet;
|
||||
|
||||
handshake.RegionInfo.SimOwner = (MasterAgent != null ? MasterAgent.AgentID : UUID.Zero);
|
||||
|
||||
// TODO: Need more methods to manipulate the scene so we can apply these properties.
|
||||
// Right now this only gets sent out to people who are logged in when the master avatar
|
||||
// is already logged in
|
||||
server.UDP.BroadcastPacket(handshake, PacketCategory.Transaction);
|
||||
}
|
||||
|
||||
#region Simian client packet handlers
|
||||
|
||||
void ChatFromViewerHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ChatFromViewerPacket chat = (ChatFromViewerPacket)packet;
|
||||
|
||||
// Forward chat from the viewer to the foreign simulator
|
||||
string message = String.Format("<{0} {1}> {2}", agent.FirstName, agent.LastName,
|
||||
Utils.BytesToString(chat.ChatData.Message));
|
||||
|
||||
client.Self.Chat(message, chat.ChatData.Channel, (ChatType)chat.ChatData.Type);
|
||||
}
|
||||
|
||||
void AgentUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentUpdatePacket update = (AgentUpdatePacket)packet;
|
||||
|
||||
// Forward AgentUpdate packets with the AgentID/SessionID set to the bots ID
|
||||
update.AgentData.AgentID = client.Self.AgentID;
|
||||
update.AgentData.SessionID = client.Self.SessionID;
|
||||
client.Network.SendPacket(update);
|
||||
if (MasterAgent == null)
|
||||
{
|
||||
lock (loginLock)
|
||||
{
|
||||
// Double-checked locking to avoid hitting the loginLock each time
|
||||
if (MasterAgent == null &&
|
||||
server.Agents.TryGetValue(update.AgentData.AgentID, out MasterAgent))
|
||||
{
|
||||
Logger.Log(String.Format("[Periscope] {0} {1} is the controlling agent",
|
||||
MasterAgent.FirstName, MasterAgent.LastName), Helpers.LogLevel.Info);
|
||||
|
||||
LoginParams login = client.Network.DefaultLoginParams(FIRST_NAME, LAST_NAME, PASSWORD, "Simian Periscope",
|
||||
"1.0.0");
|
||||
login.Start = NetworkManager.StartLocation(SIMULATOR, 128, 128, 128);
|
||||
client.Network.Login(login);
|
||||
|
||||
if (client.Network.Connected)
|
||||
Logger.Log("[Periscope] Connected: " + client.Network.LoginMessage, Helpers.LogLevel.Info);
|
||||
else
|
||||
Logger.Log("[Periscope] Failed to connect to the foreign grid: " + client.Network.LoginErrorKey, Helpers.LogLevel.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MasterAgent == null || update.AgentData.AgentID == MasterAgent.AgentID)
|
||||
{
|
||||
// Forward AgentUpdate packets with the AgentID/SessionID set to the bots ID
|
||||
update.AgentData.AgentID = client.Self.AgentID;
|
||||
update.AgentData.SessionID = client.Self.SessionID;
|
||||
client.Network.SendPacket(update);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Simian client packet handlers
|
||||
}
|
||||
}
|
||||
|
||||
205
Programs/Simian/Extensions/PeriscopeImageDelivery.cs
Normal file
205
Programs/Simian/Extensions/PeriscopeImageDelivery.cs
Normal file
@@ -0,0 +1,205 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class PeriscopeImageDelivery
|
||||
{
|
||||
Simian server;
|
||||
GridClient client;
|
||||
TexturePipeline pipeline;
|
||||
Dictionary<UUID, ImageDownload> currentDownloads = new Dictionary<UUID, ImageDownload>();
|
||||
|
||||
public PeriscopeImageDelivery(Simian server, GridClient client)
|
||||
{
|
||||
this.server = server;
|
||||
this.client = client;
|
||||
|
||||
pipeline = new TexturePipeline(client, 10);
|
||||
pipeline.OnDownloadFinished += new TexturePipeline.DownloadFinishedCallback(pipeline_OnDownloadFinished);
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.RequestImage, RequestImageHandler);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
pipeline.Shutdown();
|
||||
}
|
||||
|
||||
void RequestImageHandler(Packet packet, Agent agent)
|
||||
{
|
||||
RequestImagePacket request = (RequestImagePacket)packet;
|
||||
|
||||
for (int i = 0; i < request.RequestImage.Length; i++)
|
||||
{
|
||||
RequestImagePacket.RequestImageBlock block = request.RequestImage[i];
|
||||
|
||||
ImageDownload download;
|
||||
bool downloadFound = currentDownloads.TryGetValue(block.Image, out download);
|
||||
|
||||
if (downloadFound)
|
||||
{
|
||||
lock (download)
|
||||
{
|
||||
if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
|
||||
Logger.DebugLog(String.Format("Image download {0} is aborting", block.Image));
|
||||
|
||||
// Update download
|
||||
download.Update(block.DiscardLevel, block.DownloadPriority, (int)block.Packet);
|
||||
}
|
||||
}
|
||||
else if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
|
||||
{
|
||||
// Aborting a download we are not tracking, ignore
|
||||
}
|
||||
else
|
||||
{
|
||||
bool bake = ((ImageType)block.Type == ImageType.Baked);
|
||||
|
||||
// New download, check if we have this image
|
||||
Asset asset;
|
||||
if (server.Assets.TryGetAsset(block.Image, out asset) && asset is AssetTexture)
|
||||
{
|
||||
SendTexture(agent, (AssetTexture)asset, block.DiscardLevel, (int)block.Packet, block.DownloadPriority);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't have this texture, add it to the download queue and see if the bot can get it for us
|
||||
download = new ImageDownload(null, agent, block.DiscardLevel, block.DownloadPriority, (int)block.Packet);
|
||||
lock (currentDownloads)
|
||||
currentDownloads[block.Image] = download;
|
||||
|
||||
pipeline.RequestTexture(block.Image, (ImageType)block.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void pipeline_OnDownloadFinished(UUID id, bool success)
|
||||
{
|
||||
ImageDownload download;
|
||||
if (currentDownloads.TryGetValue(id, out download))
|
||||
{
|
||||
lock (currentDownloads)
|
||||
currentDownloads.Remove(id);
|
||||
|
||||
if (success)
|
||||
{
|
||||
// Set the texture to the downloaded texture data
|
||||
AssetTexture texture = new AssetTexture(id, pipeline.GetTextureToRender(id).AssetData);
|
||||
download.Texture = texture;
|
||||
|
||||
pipeline.RemoveFromPipeline(id);
|
||||
|
||||
// Store this texture in the local asset store for later
|
||||
server.Assets.StoreAsset(texture);
|
||||
|
||||
SendTexture(download.Agent, download.Texture, download.DiscardLevel, download.CurrentPacket, download.Priority);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("[Periscope] Failed to download texture " + id.ToString(), Helpers.LogLevel.Warning);
|
||||
|
||||
ImageNotInDatabasePacket notfound = new ImageNotInDatabasePacket();
|
||||
notfound.ImageID.ID = id;
|
||||
server.UDP.SendPacket(download.Agent.AgentID, notfound, PacketCategory.Texture);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("[Periscope] Pipeline downloaded a texture we're not tracking, " + id.ToString(), Helpers.LogLevel.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void SendTexture(Agent agent, AssetTexture texture, int discardLevel, int packet, float priority)
|
||||
{
|
||||
ImageDownload download = new ImageDownload(texture, agent, discardLevel, priority, packet);
|
||||
|
||||
Logger.DebugLog(String.Format(
|
||||
"[Periscope] Starting new texture transfer for {0}, DiscardLevel: {1}, Priority: {2}, Start: {3}, End: {4}, Total: {5}",
|
||||
texture.AssetID, discardLevel, priority, download.CurrentPacket, download.StopPacket, download.TexturePacketCount()));
|
||||
|
||||
// Send initial data
|
||||
ImageDataPacket data = new ImageDataPacket();
|
||||
data.ImageID.Codec = (byte)ImageCodec.J2C;
|
||||
data.ImageID.ID = download.Texture.AssetID;
|
||||
data.ImageID.Packets = (ushort)download.TexturePacketCount();
|
||||
data.ImageID.Size = (uint)download.Texture.AssetData.Length;
|
||||
|
||||
// The first bytes of the image are always sent in the ImageData packet
|
||||
data.ImageData = new ImageDataPacket.ImageDataBlock();
|
||||
int imageDataSize = (download.Texture.AssetData.Length >= ImageDownload.FIRST_IMAGE_PACKET_SIZE) ?
|
||||
ImageDownload.FIRST_IMAGE_PACKET_SIZE : download.Texture.AssetData.Length;
|
||||
try
|
||||
{
|
||||
data.ImageData.Data = new byte[imageDataSize];
|
||||
Buffer.BlockCopy(download.Texture.AssetData, 0, data.ImageData.Data, 0, imageDataSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format("{0}: imageDataSize={1}", ex.Message, imageDataSize),
|
||||
Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, data, PacketCategory.Texture);
|
||||
|
||||
// Check if ImagePacket packets need to be sent to complete this transfer
|
||||
if (download.CurrentPacket <= download.StopPacket)
|
||||
{
|
||||
// Insert this download into the dictionary
|
||||
lock (currentDownloads)
|
||||
currentDownloads[texture.AssetID] = download;
|
||||
|
||||
// Send all of the remaining packets
|
||||
ThreadPool.QueueUserWorkItem(
|
||||
delegate(object obj)
|
||||
{
|
||||
while (download.CurrentPacket <= download.StopPacket)
|
||||
{
|
||||
if (download.Priority == 0.0f && download.DiscardLevel == -1)
|
||||
break;
|
||||
|
||||
lock (download)
|
||||
{
|
||||
int imagePacketSize = (download.CurrentPacket == download.TexturePacketCount() - 1) ?
|
||||
download.LastPacketSize() : ImageDownload.IMAGE_PACKET_SIZE;
|
||||
|
||||
ImagePacketPacket transfer = new ImagePacketPacket();
|
||||
transfer.ImageID.ID = texture.AssetID;
|
||||
transfer.ImageID.Packet = (ushort)download.CurrentPacket;
|
||||
transfer.ImageData.Data = new byte[imagePacketSize];
|
||||
|
||||
try
|
||||
{
|
||||
Buffer.BlockCopy(download.Texture.AssetData, download.CurrentBytePosition(),
|
||||
transfer.ImageData.Data, 0, imagePacketSize);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.Log(String.Format(
|
||||
"{0}: CurrentBytePosition()={1}, AssetData.Length={2} imagePacketSize={3}",
|
||||
ex.Message, download.CurrentBytePosition(), download.Texture.AssetData.Length,
|
||||
imagePacketSize), Helpers.LogLevel.Error);
|
||||
}
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, transfer, PacketCategory.Texture);
|
||||
|
||||
++download.CurrentPacket;
|
||||
}
|
||||
}
|
||||
|
||||
Logger.DebugLog("Completed image transfer for " + texture.AssetID.ToString());
|
||||
|
||||
// Transfer is complete, remove the reference
|
||||
lock (currentDownloads)
|
||||
currentDownloads.Remove(texture.AssetID);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
371
Programs/Simian/Extensions/PeriscopeMovement.cs
Normal file
371
Programs/Simian/Extensions/PeriscopeMovement.cs
Normal file
@@ -0,0 +1,371 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class PeriscopeMovement
|
||||
{
|
||||
const int UPDATE_ITERATION = 100; //rate in milliseconds to send ObjectUpdate
|
||||
const bool ENVIRONMENT_SOUNDS = true; //collision sounds, splashing, etc
|
||||
const float GRAVITY = 9.8f; //meters/sec
|
||||
const float WALK_SPEED = 3f; //meters/sec
|
||||
const float RUN_SPEED = 5f; //meters/sec
|
||||
const float FLY_SPEED = 10f; //meters/sec
|
||||
const float FALL_DELAY = 0.33f; //seconds before starting animation
|
||||
const float FALL_FORGIVENESS = 0.25f; //fall buffer in meters
|
||||
const float JUMP_IMPULSE_VERTICAL = 8.5f; //boost amount in meters/sec
|
||||
const float JUMP_IMPULSE_HORIZONTAL = 10f; //boost amount in meters/sec (no clue why this is so high)
|
||||
const float INITIAL_HOVER_IMPULSE = 2f; //boost amount in meters/sec
|
||||
const float PREJUMP_DELAY = 0.25f; //seconds before actually jumping
|
||||
const float AVATAR_TERMINAL_VELOCITY = 54f; //~120mph
|
||||
|
||||
static readonly UUID BIG_SPLASH_SOUND = new UUID("486475b9-1460-4969-871e-fad973b38015");
|
||||
|
||||
const float SQRT_TWO = 1.41421356f;
|
||||
|
||||
Simian server;
|
||||
Periscope periscope;
|
||||
Timer updateTimer;
|
||||
long lastTick;
|
||||
|
||||
public int LastTick
|
||||
{
|
||||
get { return (int)Interlocked.Read(ref lastTick); }
|
||||
set { Interlocked.Exchange(ref lastTick, value); }
|
||||
}
|
||||
|
||||
public PeriscopeMovement(Simian server, Periscope periscope)
|
||||
{
|
||||
this.server = server;
|
||||
this.periscope = periscope;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
|
||||
|
||||
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
|
||||
LastTick = Environment.TickCount;
|
||||
updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
if (updateTimer != null)
|
||||
{
|
||||
updateTimer.Dispose();
|
||||
updateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// Don't handle movement for the master agent or foreign agents
|
||||
if (agent != periscope.MasterAgent && agent.SessionID != UUID.Zero)
|
||||
{
|
||||
bool animsChanged = false;
|
||||
|
||||
// Create forward and left vectors from the current avatar rotation
|
||||
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Rotation);
|
||||
Vector3 fwd = Vector3.Transform(Vector3.UnitX, rotMatrix);
|
||||
Vector3 left = Vector3.Transform(Vector3.UnitY, rotMatrix);
|
||||
|
||||
// Check control flags
|
||||
bool heldForward = (agent.ControlFlags & AgentManager.ControlFlags.AGENT_CONTROL_AT_POS) == AgentManager.ControlFlags.AGENT_CONTROL_AT_POS;
|
||||
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;
|
||||
|
||||
// direction in which the avatar is trying to move
|
||||
Vector3 move = Vector3.Zero;
|
||||
if (heldForward) { move.X += fwd.X; move.Y += fwd.Y; }
|
||||
if (heldBack) { move.X -= fwd.X; move.Y -= fwd.Y; }
|
||||
if (heldLeft) { move.X += left.X; move.Y += left.Y; }
|
||||
if (heldRight) { move.X -= left.X; move.Y -= left.Y; }
|
||||
if (heldUp) { move.Z += 1; }
|
||||
if (heldDown) { move.Z -= 1; }
|
||||
|
||||
// is the avatar trying to move?
|
||||
bool moving = move != Vector3.Zero;
|
||||
bool jumping = agent.TickJump != 0;
|
||||
|
||||
// 2-dimensional speed multipler
|
||||
float speed = seconds * (flying ? FLY_SPEED : agent.Running && !jumping ? RUN_SPEED : WALK_SPEED);
|
||||
if ((heldForward || heldBack) && (heldLeft || heldRight))
|
||||
speed /= SQRT_TWO;
|
||||
|
||||
// adjust multiplier for Z dimension
|
||||
float oldFloor = GetLandHeightAt(agent.Avatar.Position);
|
||||
float newFloor = GetLandHeightAt(agent.Avatar.Position + (move * speed));
|
||||
if (!flying && newFloor != oldFloor)
|
||||
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
|
||||
|
||||
// least possible distance from avatar to the ground
|
||||
// TODO: calculate to get rid of "bot squat"
|
||||
float lowerLimit = newFloor + agent.Avatar.Scale.Z / 2;
|
||||
|
||||
// Z acceleration resulting from gravity
|
||||
float gravity = 0f;
|
||||
|
||||
float waterChestHeight = server.Scene.WaterHeight - (agent.Avatar.Scale.Z * .33f);
|
||||
|
||||
if (flying)
|
||||
{
|
||||
agent.TickFall = 0;
|
||||
agent.TickJump = 0;
|
||||
|
||||
//velocity falloff while flying
|
||||
agent.Avatar.Velocity.X *= 0.66f;
|
||||
agent.Avatar.Velocity.Y *= 0.66f;
|
||||
agent.Avatar.Velocity.Z *= 0.33f;
|
||||
|
||||
if (agent.Avatar.Position.Z == lowerLimit)
|
||||
agent.Avatar.Velocity.Z += INITIAL_HOVER_IMPULSE;
|
||||
|
||||
if (move.X != 0 || move.Y != 0)
|
||||
{ //flying horizontally
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.FLY))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z > 0)
|
||||
{ //flying straight up
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (move.Z < 0)
|
||||
{ //flying straight down
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //hovering in the air
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else if (agent.Avatar.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Position.Z <= waterChestHeight)
|
||||
{ //falling, floating, or landing from a jump
|
||||
|
||||
if (agent.Avatar.Position.Z > server.Scene.WaterHeight)
|
||||
{ //above water
|
||||
|
||||
move = Vector3.Zero; //override controls while drifting
|
||||
agent.Avatar.Velocity *= 0.95f; //keep most of our inertia
|
||||
|
||||
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
|
||||
|
||||
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Velocity.Z >= 0f))
|
||||
{ //just started falling
|
||||
agent.TickFall = Environment.TickCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
gravity = GRAVITY * fallElapsed * seconds; //normal gravity
|
||||
|
||||
if (!jumping)
|
||||
{ //falling
|
||||
if (fallElapsed > FALL_DELAY)
|
||||
{ //falling long enough to trigger the animation
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //on the ground
|
||||
|
||||
agent.TickFall = 0;
|
||||
|
||||
//friction
|
||||
agent.Avatar.Acceleration *= 0.2f;
|
||||
agent.Avatar.Velocity *= 0.2f;
|
||||
|
||||
agent.Avatar.Position.Z = lowerLimit;
|
||||
|
||||
if (move.Z > 0)
|
||||
{ //jumping
|
||||
if (!jumping)
|
||||
{ //begin prejump
|
||||
move.Z = 0; //override Z control
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.PRE_JUMP))
|
||||
animsChanged = true;
|
||||
|
||||
agent.TickJump = Environment.TickCount;
|
||||
}
|
||||
else if (Environment.TickCount - agent.TickJump > PREJUMP_DELAY * 1000)
|
||||
{ //start actual jump
|
||||
|
||||
if (agent.TickJump == -1)
|
||||
{
|
||||
//already jumping! end current jump
|
||||
agent.TickJump = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
|
||||
animsChanged = true;
|
||||
|
||||
agent.Avatar.Velocity.X += agent.Avatar.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
|
||||
agent.Avatar.Velocity.Y += agent.Avatar.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
|
||||
agent.Avatar.Velocity.Z = JUMP_IMPULSE_VERTICAL * seconds;
|
||||
|
||||
agent.TickJump = -1; //flag that we are currently jumping
|
||||
}
|
||||
else
|
||||
{
|
||||
move.Z = 0; //override Z control
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //not jumping
|
||||
|
||||
agent.TickJump = 0;
|
||||
|
||||
if (move.X != 0 || move.Y != 0)
|
||||
{ //not walking
|
||||
|
||||
if (move.Z < 0)
|
||||
{ //crouchwalking
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
else if (agent.Running)
|
||||
{ //running
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.RUN))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.WALK))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //walking
|
||||
if (move.Z < 0)
|
||||
{ //crouching
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
|
||||
animsChanged = true;
|
||||
}
|
||||
else
|
||||
{ //standing
|
||||
if (server.Avatars.SetDefaultAnimation(agent, Animations.STAND))
|
||||
animsChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (animsChanged)
|
||||
server.Avatars.SendAnimations(agent);
|
||||
|
||||
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
|
||||
|
||||
// static acceleration when any control is held, otherwise none
|
||||
if (moving)
|
||||
{
|
||||
agent.Avatar.Acceleration = move * speed;
|
||||
if (agent.Avatar.Acceleration.Z < -maxVel)
|
||||
agent.Avatar.Acceleration.Z = -maxVel;
|
||||
else if (agent.Avatar.Acceleration.Z > maxVel)
|
||||
agent.Avatar.Acceleration.Z = maxVel;
|
||||
}
|
||||
else
|
||||
{
|
||||
agent.Avatar.Acceleration = Vector3.Zero;
|
||||
}
|
||||
|
||||
agent.Avatar.Velocity += agent.Avatar.Acceleration - new Vector3(0f, 0f, gravity);
|
||||
if (agent.Avatar.Velocity.Z < -maxVel)
|
||||
agent.Avatar.Velocity.Z = -maxVel;
|
||||
else if (agent.Avatar.Velocity.Z > maxVel)
|
||||
agent.Avatar.Velocity.Z = maxVel;
|
||||
|
||||
agent.Avatar.Position += agent.Avatar.Velocity;
|
||||
|
||||
if (agent.Avatar.Position.X < 0) agent.Avatar.Position.X = 0f;
|
||||
else if (agent.Avatar.Position.X > 255) agent.Avatar.Position.X = 255f;
|
||||
|
||||
if (agent.Avatar.Position.Y < 0) agent.Avatar.Position.Y = 0f;
|
||||
else if (agent.Avatar.Position.Y > 255) agent.Avatar.Position.Y = 255f;
|
||||
|
||||
if (agent.Avatar.Position.Z < lowerLimit) agent.Avatar.Position.Z = lowerLimit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AgentUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentUpdatePacket update = (AgentUpdatePacket)packet;
|
||||
|
||||
// Don't use the local physics to update the master agent
|
||||
if (agent != periscope.MasterAgent)
|
||||
{
|
||||
agent.Avatar.Rotation = update.AgentData.BodyRotation;
|
||||
agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
|
||||
agent.State = update.AgentData.State;
|
||||
agent.Flags = (PrimFlags)update.AgentData.Flags;
|
||||
}
|
||||
|
||||
ObjectUpdatePacket fullUpdate = SimulationObject.BuildFullUpdate(agent.Avatar,
|
||||
server.RegionHandle, agent.State, agent.Flags);
|
||||
|
||||
server.UDP.BroadcastPacket(fullUpdate, PacketCategory.State);
|
||||
}
|
||||
|
||||
void SetAlwaysRunHandler(Packet packet, Agent agent)
|
||||
{
|
||||
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
|
||||
|
||||
agent.Running = run.AgentData.AlwaysRun;
|
||||
}
|
||||
|
||||
float GetLandHeightAt(Vector3 position)
|
||||
{
|
||||
int x = (int)position.X;
|
||||
int y = (int)position.Y;
|
||||
|
||||
if (x > 255) x = 255;
|
||||
else if (x < 0) x = 0;
|
||||
if (y > 255) y = 255;
|
||||
else if (y < 0) y = 0;
|
||||
|
||||
float center = server.Scene.Heightmap[y * 256 + x];
|
||||
float distX = position.X - (int)position.X;
|
||||
float distY = position.Y - (int)position.Y;
|
||||
|
||||
float nearestX;
|
||||
float nearestY;
|
||||
|
||||
if (distX > 0) nearestX = server.Scene.Heightmap[y * 256 + x + (x < 255 ? 1 : 0)];
|
||||
else nearestX = server.Scene.Heightmap[y * 256 + x - (x > 0 ? 1 : 0)];
|
||||
|
||||
if (distY > 0) nearestY = server.Scene.Heightmap[(y + (y < 255 ? 1 : 0)) * 256 + x];
|
||||
else nearestY = server.Scene.Heightmap[(y - (y > 0 ? 1 : 0)) * 256 + x];
|
||||
|
||||
float lerpX = Utils.Lerp(center, nearestX, Math.Abs(distX));
|
||||
float lerpY = Utils.Lerp(center, nearestY, Math.Abs(distY));
|
||||
|
||||
return ((lerpX + lerpY) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,25 +61,23 @@ namespace Simian.Extensions
|
||||
{
|
||||
// Check if the object already exists in the scene
|
||||
if (sceneObjects.ContainsKey(obj.Prim.ID))
|
||||
{
|
||||
ObjectModify(sender, obj, obj.Prim.PrimData);
|
||||
return false;
|
||||
}
|
||||
sceneObjects.Remove(obj.Prim.LocalID, obj.Prim.ID);
|
||||
|
||||
// Assign a unique LocalID to this object
|
||||
obj.Prim.LocalID = (uint)Interlocked.Increment(ref currentLocalID);
|
||||
if (obj.Prim.LocalID == 0)
|
||||
{
|
||||
// Assign a unique LocalID to this object
|
||||
obj.Prim.LocalID = (uint)Interlocked.Increment(ref currentLocalID);
|
||||
}
|
||||
|
||||
if (OnObjectAdd != null)
|
||||
{
|
||||
OnObjectAdd(sender, obj, creatorFlags);
|
||||
}
|
||||
|
||||
// Add the object to the scene dictionary
|
||||
sceneObjects.Add(obj.Prim.LocalID, obj.Prim.ID, obj);
|
||||
|
||||
// Send an update out to the creator
|
||||
if (server.Agents.ContainsKey(obj.Prim.OwnerID))
|
||||
{
|
||||
// Send an update out to the creator
|
||||
ObjectUpdatePacket updateToOwner = SimulationObject.BuildFullUpdate(obj.Prim, server.RegionHandle, 0,
|
||||
obj.Prim.Flags | creatorFlags);
|
||||
server.UDP.SendPacket(obj.Prim.OwnerID, updateToOwner, PacketCategory.State);
|
||||
@@ -100,45 +98,52 @@ namespace Simian.Extensions
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ObjectRemove(object sender, SimulationObject obj)
|
||||
public bool ObjectRemove(object sender, uint localID)
|
||||
{
|
||||
if (OnObjectRemove != null)
|
||||
SimulationObject obj;
|
||||
if (sceneObjects.TryGetValue(localID, out obj))
|
||||
{
|
||||
OnObjectRemove(sender, obj);
|
||||
if (OnObjectRemove != null)
|
||||
OnObjectRemove(sender, obj);
|
||||
|
||||
sceneObjects.Remove(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;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public void ObjectTransform(object sender, SimulationObject obj, Vector3 position,
|
||||
Quaternion rotation, Vector3 velocity, Vector3 acceleration, Vector3 angularVelocity,
|
||||
Vector3 scale)
|
||||
public void ObjectTransform(object sender, uint localID, Vector3 position, Quaternion rotation,
|
||||
Vector3 velocity, Vector3 acceleration, Vector3 angularVelocity)
|
||||
{
|
||||
if (OnObjectTransform != null)
|
||||
SimulationObject obj;
|
||||
if (sceneObjects.TryGetValue(localID, out obj))
|
||||
{
|
||||
OnObjectTransform(sender, obj, position, rotation, velocity,
|
||||
acceleration, angularVelocity, scale);
|
||||
if (OnObjectTransform != null)
|
||||
{
|
||||
OnObjectTransform(sender, obj, position, rotation, velocity,
|
||||
acceleration, angularVelocity);
|
||||
}
|
||||
|
||||
// Update the object
|
||||
obj.Prim.Position = position;
|
||||
obj.Prim.Rotation = rotation;
|
||||
obj.Prim.Velocity = velocity;
|
||||
obj.Prim.Acceleration = acceleration;
|
||||
obj.Prim.AngularVelocity = angularVelocity;
|
||||
|
||||
// Inform clients
|
||||
BroadcastObjectUpdate(obj);
|
||||
}
|
||||
|
||||
// Update the object
|
||||
obj.Prim.Position = position;
|
||||
obj.Prim.Rotation = rotation;
|
||||
obj.Prim.Velocity = velocity;
|
||||
obj.Prim.Acceleration = acceleration;
|
||||
obj.Prim.AngularVelocity = angularVelocity;
|
||||
obj.Prim.Scale = scale;
|
||||
|
||||
// Inform clients
|
||||
BroadcastObjectUpdate(obj);
|
||||
}
|
||||
|
||||
public void ObjectFlags(object sender, SimulationObject obj, PrimFlags flags)
|
||||
@@ -170,18 +175,22 @@ namespace Simian.Extensions
|
||||
BroadcastObjectUpdate(obj);
|
||||
}
|
||||
|
||||
public void ObjectModify(object sender, SimulationObject obj, Primitive.ConstructionData data)
|
||||
public void ObjectModify(object sender, uint localID, Primitive.ConstructionData data)
|
||||
{
|
||||
if (OnObjectModify != null)
|
||||
SimulationObject obj;
|
||||
if (sceneObjects.TryGetValue(localID, out obj))
|
||||
{
|
||||
OnObjectModify(sender, obj, data);
|
||||
if (OnObjectModify != null)
|
||||
{
|
||||
OnObjectModify(sender, obj, data);
|
||||
}
|
||||
|
||||
// Update the object
|
||||
obj.Prim.PrimData = data;
|
||||
|
||||
// Inform clients
|
||||
BroadcastObjectUpdate(obj);
|
||||
}
|
||||
|
||||
// Update the object
|
||||
obj.Prim.PrimData = data;
|
||||
|
||||
// Inform clients
|
||||
BroadcastObjectUpdate(obj);
|
||||
}
|
||||
|
||||
public void AvatarAppearance(object sender, Agent agent, Primitive.TextureEntry textures, byte[] visualParams)
|
||||
@@ -191,24 +200,28 @@ namespace Simian.Extensions
|
||||
OnAvatarAppearance(sender, agent, textures, visualParams);
|
||||
}
|
||||
|
||||
// Update the avatar
|
||||
agent.Avatar.Textures = textures;
|
||||
if (visualParams != null)
|
||||
agent.VisualParams = visualParams;
|
||||
|
||||
// Broadcast the object update
|
||||
// Broadcast an object update for this avatar
|
||||
// TODO: Is this necessary here?
|
||||
ObjectUpdatePacket update = SimulationObject.BuildFullUpdate(agent.Avatar,
|
||||
server.RegionHandle, agent.State, agent.Flags);
|
||||
server.UDP.BroadcastPacket(update, PacketCategory.State);
|
||||
|
||||
// Send the appearance packet to all other clients
|
||||
AvatarAppearancePacket appearance = BuildAppearancePacket(agent);
|
||||
lock (server.Agents)
|
||||
// Update the avatar
|
||||
agent.Avatar.Textures = textures;
|
||||
if (visualParams != null && visualParams.Length > 1)
|
||||
agent.VisualParams = visualParams;
|
||||
|
||||
if (agent.VisualParams != null)
|
||||
{
|
||||
foreach (Agent recipient in server.Agents.Values)
|
||||
// Send the appearance packet to all other clients
|
||||
AvatarAppearancePacket appearance = BuildAppearancePacket(agent);
|
||||
lock (server.Agents)
|
||||
{
|
||||
if (recipient != agent)
|
||||
server.UDP.SendPacket(recipient.AgentID, appearance, PacketCategory.State);
|
||||
foreach (Agent recipient in server.Agents.Values)
|
||||
{
|
||||
if (recipient != agent)
|
||||
server.UDP.SendPacket(recipient.AgentID, appearance, PacketCategory.State);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -384,13 +397,17 @@ namespace Simian.Extensions
|
||||
appearance.Sender.ID = agent.AgentID;
|
||||
appearance.Sender.IsTrial = false;
|
||||
|
||||
appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[218];
|
||||
for (int i = 0; i < 218; i++)
|
||||
appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[agent.VisualParams.Length];
|
||||
for (int i = 0; i < agent.VisualParams.Length; i++)
|
||||
{
|
||||
appearance.VisualParam[i] = new AvatarAppearancePacket.VisualParamBlock();
|
||||
appearance.VisualParam[i].ParamValue = agent.VisualParams[i];
|
||||
}
|
||||
|
||||
if (agent.VisualParams.Length != 218)
|
||||
Logger.Log("Built an appearance packet with VisualParams.Length=" + agent.VisualParams.Length,
|
||||
Helpers.LogLevel.Warning);
|
||||
|
||||
return appearance;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,14 +197,8 @@ namespace Simian
|
||||
{
|
||||
// Look up the UDPClient this is going to
|
||||
UDPClient client;
|
||||
if (!clients.TryGetValue(agentID, out client))
|
||||
{
|
||||
Logger.Log("Attempted to send a packet to unknown UDP client " +
|
||||
agentID.ToString(), Helpers.LogLevel.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
SendPacket(client, new OutgoingPacket(packet, category));
|
||||
if (clients.TryGetValue(agentID, out client))
|
||||
SendPacket(client, new OutgoingPacket(packet, category));
|
||||
}
|
||||
|
||||
void SendPacket(UDPClient client, OutgoingPacket outgoingPacket)
|
||||
|
||||
Reference in New Issue
Block a user