* Initial framework support for multiple simulators. Currently only the first region config is loaded, but the framework is there
* Big reorganization of config and data files
* Started renaming extensions that handle LLUDP packets to start with the LL prefix. Work in progress
* Initial SSL support

git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2482 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
John Hurliman
2009-03-12 23:02:42 +00:00
parent 627e0dbef7
commit 70fe1f8f2a
101 changed files with 3083 additions and 1746 deletions

View File

@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.StructuredData;
using OpenMetaverse.Packets;
namespace Simian
{
class AvatarManager : IExtension<ISceneProvider>, IAvatarProvider
{
ISceneProvider scene;
int currentWearablesSerialNum = -1;
int currentAnimSequenceNum = 0;
public AvatarManager()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
return true;
}
public void Stop()
{
}
public bool SetDefaultAnimation(Agent agent, UUID animID)
{
return agent.Animations.SetDefaultAnimation(animID, ref currentAnimSequenceNum);
}
public bool AddAnimation(Agent agent, UUID animID)
{
return agent.Animations.Add(animID, ref currentAnimSequenceNum);
}
public bool RemoveAnimation(Agent agent, UUID animID)
{
return agent.Animations.Remove(animID);
}
public bool ClearAnimations(Agent agent)
{
agent.Animations.Clear();
return true;
}
public void SendAnimations(Agent agent)
{
scene.ObjectAnimate(this, agent.ID, agent.ID, agent.Animations.GetAnimations());
}
public void SendAlert(Agent agent, string message)
{
AlertMessagePacket alert = new AlertMessagePacket();
alert.AlertData.Message = Utils.StringToBytes(message);
scene.UDP.SendPacket(agent.ID, alert, PacketCategory.Transaction);
}
}
}

View File

@@ -0,0 +1,87 @@
using System;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class ConnectionManagement : IExtension<ISceneProvider>
{
ISceneProvider scene;
public ConnectionManagement()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.UseCircuitCode, UseCircuitCodeHandler);
scene.UDP.RegisterPacketCallback(PacketType.StartPingCheck, StartPingCheckHandler);
scene.UDP.RegisterPacketCallback(PacketType.LogoutRequest, LogoutRequestHandler);
return true;
}
public void Stop()
{
}
void UseCircuitCodeHandler(Packet packet, Agent agent)
{
RegionHandshakePacket handshake = new RegionHandshakePacket();
handshake.RegionInfo.BillableFactor = 0f;
handshake.RegionInfo.CacheID = UUID.Random();
handshake.RegionInfo.IsEstateManager = false;
handshake.RegionInfo.RegionFlags = (uint)scene.RegionFlags;
handshake.RegionInfo.SimOwner = UUID.Random();
handshake.RegionInfo.SimAccess = (byte)SimAccess.Min;
handshake.RegionInfo.SimName = Utils.StringToBytes(scene.RegionName);
handshake.RegionInfo.WaterHeight = scene.WaterHeight;
handshake.RegionInfo.TerrainBase0 = UUID.Zero;
handshake.RegionInfo.TerrainBase1 = UUID.Zero;
handshake.RegionInfo.TerrainBase2 = UUID.Zero;
handshake.RegionInfo.TerrainBase3 = UUID.Zero;
handshake.RegionInfo.TerrainDetail0 = UUID.Zero;
handshake.RegionInfo.TerrainDetail1 = UUID.Zero;
handshake.RegionInfo.TerrainDetail2 = UUID.Zero;
handshake.RegionInfo.TerrainDetail3 = UUID.Zero;
handshake.RegionInfo.TerrainHeightRange00 = 0f;
handshake.RegionInfo.TerrainHeightRange01 = 20f;
handshake.RegionInfo.TerrainHeightRange10 = 0f;
handshake.RegionInfo.TerrainHeightRange11 = 20f;
handshake.RegionInfo.TerrainStartHeight00 = 0f;
handshake.RegionInfo.TerrainStartHeight01 = 40f;
handshake.RegionInfo.TerrainStartHeight10 = 0f;
handshake.RegionInfo.TerrainStartHeight11 = 40f;
handshake.RegionInfo2.RegionID = scene.RegionID;
scene.UDP.SendPacket(agent.ID, handshake, PacketCategory.Transaction);
}
void StartPingCheckHandler(Packet packet, Agent agent)
{
StartPingCheckPacket start = (StartPingCheckPacket)packet;
CompletePingCheckPacket complete = new CompletePingCheckPacket();
complete.Header.Reliable = false;
complete.PingID.PingID = start.PingID.PingID;
scene.UDP.SendPacket(agent.ID, complete, PacketCategory.Overhead);
}
void LogoutRequestHandler(Packet packet, Agent agent)
{
LogoutReplyPacket reply = new LogoutReplyPacket();
reply.AgentData.AgentID = agent.ID;
reply.AgentData.SessionID = agent.SessionID;
reply.InventoryData = new LogoutReplyPacket.InventoryDataBlock[1];
reply.InventoryData[0] = new LogoutReplyPacket.InventoryDataBlock();
reply.InventoryData[0].ItemID = UUID.Zero;
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
scene.ObjectRemove(this, agent.ID);
}
}
}

View File

@@ -0,0 +1,278 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class ImageDownload
{
public const int FIRST_IMAGE_PACKET_SIZE = 600;
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, Agent agent, int discardLevel, float priority, int packet)
{
Texture = texture;
Agent = agent;
Update(discardLevel, priority, packet);
}
/// <summary>
/// Updates an image transfer with new information and recalculates
/// offsets
/// </summary>
/// <param name="discardLevel">New requested discard level</param>
/// <param name="priority">New requested priority</param>
/// <param name="packet">New requested packet offset</param>
public void Update(int discardLevel, float priority, int packet)
{
Priority = priority;
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
{
// No layers, send the full image
DiscardLevel = 0;
StopPacket = GetPacketForBytePosition(Texture.AssetData.Length - 1);
}
CurrentPacket = Utils.Clamp(packet, 1, TexturePacketCount());
}
else
{
DiscardLevel = discardLevel;
Priority = priority;
CurrentPacket = packet;
}
}
/// <summary>
/// Returns the total number of packets needed to transfer this texture,
/// including the first packet of size FIRST_IMAGE_PACKET_SIZE
/// </summary>
/// <returns>Total number of packets needed to transfer this texture</returns>
public int TexturePacketCount()
{
return ((Texture.AssetData.Length - FIRST_IMAGE_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE) + 1;
}
/// <summary>
/// Returns the current byte offset for this transfer, calculated from
/// the CurrentPacket
/// </summary>
/// <returns>Current byte offset for this transfer</returns>
public int CurrentBytePosition()
{
return FIRST_IMAGE_PACKET_SIZE + (CurrentPacket - 1) * IMAGE_PACKET_SIZE;
}
/// <summary>
/// Returns the size, in bytes, of the last packet. This will be somewhere
/// between 1 and IMAGE_PACKET_SIZE bytes
/// </summary>
/// <returns>Size of the last packet in the transfer</returns>
public int LastPacketSize()
{
return Texture.AssetData.Length - (FIRST_IMAGE_PACKET_SIZE + ((TexturePacketCount() - 2) * IMAGE_PACKET_SIZE));
}
/// <summary>
/// Find the packet number that contains a given byte position
/// </summary>
/// <param name="bytePosition">Byte position</param>
/// <returns>Packet number that contains the given byte position</returns>
int GetPacketForBytePosition(int bytePosition)
{
return ((bytePosition - FIRST_IMAGE_PACKET_SIZE + IMAGE_PACKET_SIZE - 1) / IMAGE_PACKET_SIZE);
}
}
public class ImageDelivery : IExtension<ISceneProvider>
{
ISceneProvider scene;
Dictionary<UUID, ImageDownload> CurrentDownloads = new Dictionary<UUID, ImageDownload>();
public ImageDelivery()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.RequestImage, RequestImageHandler);
return true;
}
public void Stop()
{
}
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));
}
else
{
if (block.DiscardLevel < download.DiscardLevel)
Logger.DebugLog(String.Format("Image download {0} is changing from DiscardLevel {1} to {2}",
block.Image, download.DiscardLevel, block.DiscardLevel));
if (block.DownloadPriority != download.Priority)
Logger.DebugLog(String.Format("Image download {0} is changing from Priority {1} to {2}",
block.Image, download.Priority, block.DownloadPriority));
if (block.Packet != download.CurrentPacket)
Logger.DebugLog(String.Format("Image download {0} is changing from Packet {1} to {2}",
block.Image, download.CurrentPacket, block.Packet));
}
// 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 (scene.Server.Assets.TryGetAsset(block.Image, out asset) && asset is AssetTexture)
{
SendTexture(agent, (AssetTexture)asset, block.DiscardLevel, (int)block.Packet, block.DownloadPriority);
}
else
{
Logger.Log("Request for a missing texture " + block.Image.ToString(), Helpers.LogLevel.Warning);
ImageNotInDatabasePacket notfound = new ImageNotInDatabasePacket();
notfound.ImageID.ID = block.Image;
scene.UDP.SendPacket(agent.ID, notfound, PacketCategory.Texture);
}
}
}
}
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);
}
scene.UDP.SendPacket(agent.ID, 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);
}
scene.UDP.SendPacket(agent.ID, 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);
}
);
}
}
}
}

View File

@@ -0,0 +1,406 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class LLAgents : IExtension<ISceneProvider>
{
ISceneProvider scene;
int currentWearablesSerialNum = -1;
int currentAnimSequenceNum = 0;
Timer coarseLocationTimer;
public LLAgents()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.AvatarPropertiesRequest, AvatarPropertiesRequestHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentWearablesRequest, AgentWearablesRequestHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentIsNowWearing, AgentIsNowWearingHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentSetAppearance, AgentSetAppearanceHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentCachedTexture, AgentCachedTextureHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentHeightWidth, AgentHeightWidthHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentAnimation, AgentAnimationHandler);
scene.UDP.RegisterPacketCallback(PacketType.SoundTrigger, SoundTriggerHandler);
scene.UDP.RegisterPacketCallback(PacketType.ViewerEffect, ViewerEffectHandler);
scene.UDP.RegisterPacketCallback(PacketType.UUIDNameRequest, UUIDNameRequestHandler);
if (coarseLocationTimer != null) coarseLocationTimer.Dispose();
coarseLocationTimer = new Timer(coarseLocationTimer_Elapsed);
coarseLocationTimer.Change(1000, 1000);
return true;
}
public void Stop()
{
if (coarseLocationTimer != null)
{
coarseLocationTimer.Dispose();
coarseLocationTimer = null;
}
}
void AgentAnimationHandler(Packet packet, Agent agent)
{
AgentAnimationPacket animPacket = (AgentAnimationPacket)packet;
bool changed = false;
for (int i = 0; i < animPacket.AnimationList.Length; i++)
{
AgentAnimationPacket.AnimationListBlock block = animPacket.AnimationList[i];
if (block.StartAnim)
{
if (agent.Animations.Add(block.AnimID, ref currentAnimSequenceNum))
changed = true;
}
else
{
if (agent.Animations.Remove(block.AnimID))
changed = true;
}
}
if (changed)
scene.Avatars.SendAnimations(agent);
}
void ViewerEffectHandler(Packet packet, Agent agent)
{
ViewerEffectPacket incomingEffects = (ViewerEffectPacket)packet;
ViewerEffect[] outgoingEffects = new ViewerEffect[incomingEffects.Effect.Length];
for (int i = 0; i < outgoingEffects.Length; i++)
{
ViewerEffectPacket.EffectBlock block = incomingEffects.Effect[i];
outgoingEffects[i] = new ViewerEffect(block.ID, (EffectType)block.Type, block.AgentID,
new Color4(block.Color, 0, true), block.Duration, block.TypeData);
}
scene.TriggerEffects(this, outgoingEffects);
}
void AvatarPropertiesRequestHandler(Packet packet, Agent agent)
{
AvatarPropertiesRequestPacket request = (AvatarPropertiesRequestPacket)packet;
Agent foundAgent;
if (scene.TryGetAgent(request.AgentData.AvatarID, out foundAgent))
{
AvatarPropertiesReplyPacket reply = new AvatarPropertiesReplyPacket();
reply.AgentData.AgentID = agent.ID;
reply.AgentData.AvatarID = request.AgentData.AvatarID;
reply.PropertiesData.AboutText = Utils.StringToBytes(foundAgent.Info.ProfileAboutText);
reply.PropertiesData.BornOn = Utils.StringToBytes(foundAgent.Info.ProfileBornOn);
reply.PropertiesData.CharterMember = new byte[1];
reply.PropertiesData.FLAboutText = Utils.StringToBytes(foundAgent.Info.ProfileFirstText);
reply.PropertiesData.Flags = (uint)foundAgent.Info.ProfileFlags;
reply.PropertiesData.FLImageID = foundAgent.Info.ProfileFirstImage;
reply.PropertiesData.ImageID = foundAgent.Info.ProfileImage;
reply.PropertiesData.PartnerID = foundAgent.Info.PartnerID;
reply.PropertiesData.ProfileURL = Utils.StringToBytes(foundAgent.Info.ProfileURL);
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
}
else
{
Logger.Log("AvatarPropertiesRequest for unknown agent " + request.AgentData.AvatarID.ToString(),
Helpers.LogLevel.Warning);
}
}
void AgentWearablesRequestHandler(Packet packet, Agent agent)
{
SendWearables(agent);
}
void AgentIsNowWearingHandler(Packet packet, Agent agent)
{
AgentIsNowWearingPacket wearing = (AgentIsNowWearingPacket)packet;
for (int i = 0; i < wearing.WearableData.Length; i++)
{
UUID itemID = wearing.WearableData[i].ItemID;
#region Update Wearables
switch ((WearableType)wearing.WearableData[i].WearableType)
{
case WearableType.Shape:
agent.Info.ShapeItem = itemID;
break;
case WearableType.Skin:
agent.Info.SkinItem = itemID;
break;
case WearableType.Hair:
agent.Info.HairItem = itemID;
break;
case WearableType.Eyes:
agent.Info.EyesItem = itemID;
break;
case WearableType.Shirt:
agent.Info.ShirtItem = itemID;
break;
case WearableType.Pants:
agent.Info.PantsItem = itemID;
break;
case WearableType.Shoes:
agent.Info.ShoesItem = itemID;
break;
case WearableType.Socks:
agent.Info.SocksItem = itemID;
break;
case WearableType.Jacket:
agent.Info.JacketItem = itemID;
break;
case WearableType.Gloves:
agent.Info.GlovesItem = itemID;
break;
case WearableType.Undershirt:
agent.Info.UndershirtItem = itemID;
break;
case WearableType.Underpants:
agent.Info.UnderpantsItem = itemID;
break;
case WearableType.Skirt:
agent.Info.SkirtItem = itemID;
break;
}
#endregion Update Wearables
}
// FIXME: GetCurrentWearables() is a very expensive call, remove it from this debug line
Logger.DebugLog("Updated agent wearables, new count: " + GetCurrentWearables(agent).Count);
}
void AgentSetAppearanceHandler(Packet packet, Agent agent)
{
AgentSetAppearancePacket set = (AgentSetAppearancePacket)packet;
Logger.DebugLog("Updating avatar appearance");
//TODO: Store this for cached bake responses
for (int i = 0; i < set.WearableData.Length; i++)
{
AppearanceManager.TextureIndex index = (AppearanceManager.TextureIndex)set.WearableData[i].TextureIndex;
UUID cacheID = set.WearableData[i].CacheID;
Logger.DebugLog(String.Format("WearableData: {0} is now {1}", index, cacheID));
}
// Create a TextureEntry
Primitive.TextureEntry textureEntry = new Primitive.TextureEntry(set.ObjectData.TextureEntry, 0,
set.ObjectData.TextureEntry.Length);
// Create a block of VisualParams
byte[] visualParams = new byte[set.VisualParam.Length];
for (int i = 0; i < set.VisualParam.Length; i++)
visualParams[i] = set.VisualParam[i].ParamValue;
scene.AgentAppearance(this, agent, textureEntry, visualParams);
}
void AgentCachedTextureHandler(Packet packet, Agent agent)
{
AgentCachedTexturePacket cached = (AgentCachedTexturePacket)packet;
AgentCachedTextureResponsePacket response = new AgentCachedTextureResponsePacket();
response.AgentData.AgentID = agent.ID;
response.AgentData.SerialNum = cached.AgentData.SerialNum;
response.WearableData = new AgentCachedTextureResponsePacket.WearableDataBlock[cached.WearableData.Length];
// TODO: Respond back with actual cache entries if we have them
for (int i = 0; i < cached.WearableData.Length; i++)
{
response.WearableData[i] = new AgentCachedTextureResponsePacket.WearableDataBlock();
response.WearableData[i].TextureIndex = cached.WearableData[i].TextureIndex;
response.WearableData[i].TextureID = UUID.Zero;
response.WearableData[i].HostName = Utils.EmptyBytes;
}
response.Header.Zerocoded = true;
scene.UDP.SendPacket(agent.ID, response, PacketCategory.Transaction);
}
void AgentHeightWidthHandler(Packet packet, Agent agent)
{
//AgentHeightWidthPacket heightWidth = (AgentHeightWidthPacket)packet;
// TODO: These are the screen size dimensions. Useful when we start doing frustum culling
//Logger.Log(String.Format("Agent wants to set height={0}, width={1}",
// heightWidth.HeightWidthBlock.Height, heightWidth.HeightWidthBlock.Width), Helpers.LogLevel.Info);
}
void SoundTriggerHandler(Packet packet, Agent agent)
{
SoundTriggerPacket trigger = (SoundTriggerPacket)packet;
TriggerSound(agent, trigger.SoundData.SoundID, trigger.SoundData.Gain);
}
void UUIDNameRequestHandler(Packet packet, Agent agent)
{
UUIDNameRequestPacket request = (UUIDNameRequestPacket)packet;
UUIDNameReplyPacket reply = new UUIDNameReplyPacket();
reply.UUIDNameBlock = new UUIDNameReplyPacket.UUIDNameBlockBlock[request.UUIDNameBlock.Length];
for (int i = 0; i < request.UUIDNameBlock.Length; i++)
{
UUID id = request.UUIDNameBlock[i].ID;
reply.UUIDNameBlock[i] = new UUIDNameReplyPacket.UUIDNameBlockBlock();
reply.UUIDNameBlock[i].ID = id;
Agent foundAgent;
if (scene.TryGetAgent(id, out foundAgent))
{
reply.UUIDNameBlock[i].FirstName = Utils.StringToBytes(foundAgent.Info.FirstName);
reply.UUIDNameBlock[i].LastName = Utils.StringToBytes(foundAgent.Info.LastName);
}
else
{
reply.UUIDNameBlock[i].FirstName = Utils.EmptyBytes;
reply.UUIDNameBlock[i].LastName = Utils.EmptyBytes;
}
}
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
}
void TriggerSound(Agent agent, UUID soundID, float gain)
{
scene.TriggerSound(this, agent.ID, agent.ID, agent.ID, soundID, agent.Avatar.Prim.Position, gain);
}
void SendWearables(Agent agent)
{
AgentWearablesUpdatePacket update = new AgentWearablesUpdatePacket();
update.AgentData.AgentID = agent.ID;
Dictionary<WearableType, InventoryItem> wearables = GetCurrentWearables(agent);
update.WearableData = new AgentWearablesUpdatePacket.WearableDataBlock[wearables.Count];
int i = 0;
foreach (KeyValuePair<WearableType, InventoryItem> kvp in wearables)
{
update.WearableData[i] = new AgentWearablesUpdatePacket.WearableDataBlock();
update.WearableData[i].AssetID = kvp.Value.AssetID;
update.WearableData[i].ItemID = kvp.Value.ID;
update.WearableData[i].WearableType = (byte)kvp.Key;
++i;
}
// Technically this should be per-agent, but if the only requirement is that it
// increments this is easier
update.AgentData.SerialNum = (uint)Interlocked.Increment(ref currentWearablesSerialNum);
Logger.DebugLog(String.Format("Sending info about {0} wearables", wearables.Count));
scene.UDP.SendPacket(agent.ID, update, PacketCategory.Asset);
}
Dictionary<WearableType, InventoryItem> GetCurrentWearables(Agent agent)
{
Dictionary<WearableType, InventoryItem> wearables = new Dictionary<WearableType, InventoryItem>();
TryAddWearable(agent.ID, wearables, WearableType.Shape, agent.Info.ShapeItem);
TryAddWearable(agent.ID, wearables, WearableType.Skin, agent.Info.SkinItem);
TryAddWearable(agent.ID, wearables, WearableType.Hair, agent.Info.HairItem);
TryAddWearable(agent.ID, wearables, WearableType.Eyes, agent.Info.EyesItem);
TryAddWearable(agent.ID, wearables, WearableType.Shirt, agent.Info.ShirtItem);
TryAddWearable(agent.ID, wearables, WearableType.Pants, agent.Info.PantsItem);
TryAddWearable(agent.ID, wearables, WearableType.Shoes, agent.Info.ShoesItem);
TryAddWearable(agent.ID, wearables, WearableType.Socks, agent.Info.SocksItem);
TryAddWearable(agent.ID, wearables, WearableType.Jacket, agent.Info.JacketItem);
TryAddWearable(agent.ID, wearables, WearableType.Gloves, agent.Info.GlovesItem);
TryAddWearable(agent.ID, wearables, WearableType.Undershirt, agent.Info.UndershirtItem);
TryAddWearable(agent.ID, wearables, WearableType.Underpants, agent.Info.UnderpantsItem);
TryAddWearable(agent.ID, wearables, WearableType.Skirt, agent.Info.SkirtItem);
return wearables;
}
bool TryAddWearable(UUID agentID, Dictionary<WearableType, InventoryItem> wearables, WearableType type, UUID itemID)
{
InventoryObject obj;
if (itemID != UUID.Zero && scene.Server.Inventory.TryGetInventory(agentID, itemID, out obj) &&
obj is InventoryItem)
{
wearables.Add(type, (InventoryItem)obj);
return true;
}
else
{
return false;
}
}
void coarseLocationTimer_Elapsed(object sender)
{
// Create lists containing all of the agent blocks
List<CoarseLocationUpdatePacket.AgentDataBlock> agentDatas = new List<CoarseLocationUpdatePacket.AgentDataBlock>();
List<CoarseLocationUpdatePacket.LocationBlock> agentLocations = new List<CoarseLocationUpdatePacket.LocationBlock>();
scene.ForEachAgent(
delegate(Agent agent)
{
CoarseLocationUpdatePacket.AgentDataBlock dataBlock = new CoarseLocationUpdatePacket.AgentDataBlock();
dataBlock.AgentID = agent.ID;
CoarseLocationUpdatePacket.LocationBlock locationBlock = new CoarseLocationUpdatePacket.LocationBlock();
locationBlock.X = (byte)agent.Avatar.Prim.Position.X;
locationBlock.Y = (byte)agent.Avatar.Prim.Position.Y;
locationBlock.Z = (byte)((int)agent.Avatar.Prim.Position.Z / 4);
agentDatas.Add(dataBlock);
agentLocations.Add(locationBlock);
}
);
// Send location updates out to each agent
scene.ForEachAgent(
delegate(Agent agent)
{
CoarseLocationUpdatePacket update = new CoarseLocationUpdatePacket();
update.Index.Prey = -1;
update.Index.You = 0;
// Count the number of blocks to send out
int count = 0;
for (int i = 0; i < agentDatas.Count; i++)
{
if (agentDatas[i].AgentID != agent.ID)
++count;
}
update.AgentData = new CoarseLocationUpdatePacket.AgentDataBlock[count];
update.Location = new CoarseLocationUpdatePacket.LocationBlock[count];
int j = 0;
for (int i = 0; i < agentDatas.Count; i++)
{
if (agentDatas[i].AgentID != agent.ID)
{
update.AgentData[j] = agentDatas[i];
update.Location[j] = agentLocations[i];
++j;
}
}
scene.UDP.SendPacket(agent.ID, update, PacketCategory.State);
}
);
}
}
}

View File

@@ -0,0 +1,683 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class LLInventory : IExtension<ISceneProvider>
{
ISceneProvider scene;
public LLInventory()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.CreateInventoryItem, CreateInventoryItemHandler);
scene.UDP.RegisterPacketCallback(PacketType.CreateInventoryFolder, CreateInventoryFolderHandler);
scene.UDP.RegisterPacketCallback(PacketType.UpdateInventoryItem, UpdateInventoryItemHandler);
scene.UDP.RegisterPacketCallback(PacketType.FetchInventoryDescendents, FetchInventoryDescendentsHandler);
scene.UDP.RegisterPacketCallback(PacketType.FetchInventory, FetchInventoryHandler);
scene.UDP.RegisterPacketCallback(PacketType.CopyInventoryItem, CopyInventoryItemHandler);
scene.UDP.RegisterPacketCallback(PacketType.MoveInventoryItem, MoveInventoryItemHandler);
scene.UDP.RegisterPacketCallback(PacketType.MoveInventoryFolder, MoveInventoryFolderHandler);
scene.UDP.RegisterPacketCallback(PacketType.PurgeInventoryDescendents, PurgeInventoryDescendentsHandler);
scene.UDP.RegisterPacketCallback(PacketType.DeRezObject, DeRezObjectHandler);
return true;
}
public void Stop()
{
}
void CreateInventoryItemHandler(Packet packet, Agent agent)
{
CreateInventoryItemPacket create = (CreateInventoryItemPacket)packet;
UUID assetID;
if (create.InventoryBlock.TransactionID != UUID.Zero)
assetID = UUID.Combine(create.InventoryBlock.TransactionID, agent.SecureSessionID);
else
assetID = UUID.Random();
UUID parentID = (create.InventoryBlock.FolderID != UUID.Zero) ? create.InventoryBlock.FolderID : agent.Info.InventoryRoot;
AssetType assetType = (AssetType)create.InventoryBlock.Type;
switch (assetType)
{
case AssetType.Gesture:
Logger.Log("Need to create a default gesture asset!", Helpers.LogLevel.Warning);
break;
}
if (parentID == UUID.Zero)
parentID = agent.Info.InventoryRoot;
// Create the inventory item
InventoryItem item = scene.Server.Inventory.CreateItem(agent.ID, Utils.BytesToString(create.InventoryBlock.Name),
"Created in Simian", (InventoryType)create.InventoryBlock.InvType, assetType, assetID, parentID,
PermissionMask.All, (PermissionMask)create.InventoryBlock.NextOwnerMask, agent.ID,
agent.ID, create.InventoryBlock.TransactionID, create.InventoryBlock.CallbackID);
// Send a success response
SendItemCreatedPacket(agent, item, create.InventoryBlock.TransactionID, create.InventoryBlock.CallbackID);
}
void CreateInventoryFolderHandler(Packet packet, Agent agent)
{
CreateInventoryFolderPacket create = (CreateInventoryFolderPacket)packet;
UUID folderID = create.FolderData.FolderID;
if (folderID == UUID.Zero)
folderID = agent.Info.InventoryRoot;
scene.Server.Inventory.CreateFolder(agent.ID, folderID, Utils.BytesToString(create.FolderData.Name),
(AssetType)create.FolderData.Type, create.FolderData.ParentID, agent.ID);
}
void UpdateInventoryItemHandler(Packet packet, Agent agent)
{
UpdateInventoryItemPacket update = (UpdateInventoryItemPacket)packet;
// No packet is sent back to the client, we just need to update the
// inventory item locally
for (int i = 0; i < update.InventoryData.Length; i++)
{
UpdateInventoryItemPacket.InventoryDataBlock block = update.InventoryData[i];
InventoryObject obj;
if (scene.Server.Inventory.TryGetInventory(agent.ID, block.ItemID, out obj) && obj is InventoryItem)
{
InventoryItem item = (InventoryItem)obj;
//item.Permissions.BaseMask = (PermissionMask)block.BaseMask;
item.Permissions.BaseMask = PermissionMask.All;
//item.Permissions.EveryoneMask = (PermissionMask)block.EveryoneMask;
item.Permissions.EveryoneMask = PermissionMask.All;
//item.Permissions.GroupMask = (PermissionMask)block.GroupMask;
item.Permissions.GroupMask = PermissionMask.All;
//item.Permissions.NextOwnerMask = (PermissionMask)block.NextOwnerMask;
item.Permissions.NextOwnerMask = PermissionMask.All;
//item.Permissions.OwnerMask = (PermissionMask)block.OwnerMask;
item.Permissions.OwnerMask = PermissionMask.All;
//block.CRC;
item.CreationDate = Utils.UnixTimeToDateTime(block.CreationDate);
item.CreatorID = block.CreatorID;
item.Name = Utils.BytesToString(block.Description);
item.Flags = block.Flags;
item.ParentID = block.FolderID;
item.GroupID = block.GroupID;
item.GroupOwned = block.GroupOwned;
item.InventoryType = (InventoryType)block.InvType;
item.Name = Utils.BytesToString(block.Name);
item.OwnerID = block.OwnerID;
item.SalePrice = block.SalePrice;
item.SaleType = (SaleType)block.SaleType;
item.AssetType = (AssetType)block.Type;
item.AssetID = UUID.Combine(block.TransactionID, agent.SecureSessionID);
Logger.DebugLog(String.Format(
"UpdateInventoryItem: CallbackID: {0}, TransactionID: {1}",
block.CallbackID, block.TransactionID));
}
else
{
Logger.Log("Received an UpdateInventoryItem packet for unknown inventory item " +
block.ItemID.ToString(), Helpers.LogLevel.Warning);
}
}
}
void FetchInventoryDescendentsHandler(Packet packet, Agent agent)
{
// A very safe estimate of the fixed minimum packet size
const int PACKET_OVERHEAD = 96;
FetchInventoryDescendentsPacket fetch = (FetchInventoryDescendentsPacket)packet;
bool sendFolders = fetch.InventoryData.FetchFolders;
bool sendItems = fetch.InventoryData.FetchItems;
// TODO: Obey SortOrder
//InventorySortOrder order = (InventorySortOrder)fetch.InventoryData.SortOrder;
// TODO: Use OwnerID
// TODO: Do we need to obey InventorySortOrder?
InventoryObject invObject;
if (scene.Server.Inventory.TryGetInventory(agent.ID, fetch.InventoryData.FolderID, out invObject) && invObject is InventoryFolder)
{
InventoryFolder folder = (InventoryFolder)invObject;
int descendCount;
int version;
List<InventoryItem> items = new List<InventoryItem>();
List<InventoryFolder> folders = new List<InventoryFolder>();
InventoryDescendentsPacket.FolderDataBlock[] folderBlocks;
InventoryDescendentsPacket.ItemDataBlock[] itemBlocks;
lock (folder.Children.Dictionary)
{
// These two are coupled to the actual items in the dictionary,
// so they are set inside the lock
descendCount = folder.Children.Count;
version = folder.Version;
if (sendItems || sendFolders)
{
// Create a list of all of the folders and items under this folder
folder.Children.ForEach(
delegate(InventoryObject obj)
{
if (obj is InventoryItem)
items.Add((InventoryItem)obj);
else
folders.Add((InventoryFolder)obj);
}
);
}
}
if (sendFolders)
{
folderBlocks = new InventoryDescendentsPacket.FolderDataBlock[folders.Count];
for (int i = 0; i < folders.Count; i++)
{
InventoryFolder currentFolder = folders[i];
folderBlocks[i] = new InventoryDescendentsPacket.FolderDataBlock();
folderBlocks[i].FolderID = currentFolder.ID;
folderBlocks[i].Name = Utils.StringToBytes(currentFolder.Name);
folderBlocks[i].ParentID = currentFolder.ParentID;
folderBlocks[i].Type = (sbyte)currentFolder.PreferredType;
}
}
else
{
folderBlocks = new InventoryDescendentsPacket.FolderDataBlock[0];
}
if (sendItems)
{
itemBlocks = new InventoryDescendentsPacket.ItemDataBlock[items.Count];
for (int i = 0; i < items.Count; i++)
{
InventoryItem currentItem = items[i];
itemBlocks[i] = new InventoryDescendentsPacket.ItemDataBlock();
itemBlocks[i].AssetID = currentItem.AssetID;
itemBlocks[i].BaseMask = (uint)currentItem.Permissions.BaseMask;
itemBlocks[i].CRC = currentItem.CRC;
itemBlocks[i].CreationDate = (int)Utils.DateTimeToUnixTime(currentItem.CreationDate);
itemBlocks[i].CreatorID = currentItem.CreatorID;
itemBlocks[i].Description = Utils.StringToBytes(currentItem.Description);
itemBlocks[i].EveryoneMask = (uint)currentItem.Permissions.EveryoneMask;
itemBlocks[i].Flags = currentItem.Flags;
itemBlocks[i].FolderID = currentItem.ParentID;
itemBlocks[i].GroupID = currentItem.GroupID;
itemBlocks[i].GroupMask = (uint)currentItem.Permissions.GroupMask;
itemBlocks[i].GroupOwned = currentItem.GroupOwned;
itemBlocks[i].InvType = (sbyte)currentItem.InventoryType;
itemBlocks[i].ItemID = currentItem.ID;
itemBlocks[i].Name = Utils.StringToBytes(currentItem.Name);
itemBlocks[i].NextOwnerMask = (uint)currentItem.Permissions.NextOwnerMask;
itemBlocks[i].OwnerID = currentItem.OwnerID;
itemBlocks[i].OwnerMask = (uint)currentItem.Permissions.OwnerMask;
itemBlocks[i].SalePrice = currentItem.SalePrice;
itemBlocks[i].SaleType = (byte)currentItem.SaleType;
itemBlocks[i].Type = (sbyte)currentItem.AssetType;
}
}
else
{
itemBlocks = new InventoryDescendentsPacket.ItemDataBlock[0];
}
// FolderDataBlock and ItemDataBlock are both variable and possibly very large,
// so we handle the splitting separately. This could be replaced by some custom
// splitting
if (folderBlocks.Length > 0)
{
List<int> splitPoints = Helpers.SplitBlocks(folderBlocks, PACKET_OVERHEAD);
Logger.DebugLog(String.Format("Sending {0} InventoryDescendents packets containing {1} folders",
splitPoints.Count, folderBlocks.Length));
for (int i = 0; i < splitPoints.Count; i++)
{
int count = (i != splitPoints.Count - 1) ? splitPoints[i + 1] - splitPoints[i] :
folderBlocks.Length - splitPoints[i];
InventoryDescendentsPacket descendents = new InventoryDescendentsPacket();
descendents.AgentData.AgentID = agent.ID;
descendents.AgentData.FolderID = folder.ID;
descendents.AgentData.OwnerID = folder.OwnerID;
descendents.AgentData.Descendents = descendCount;
descendents.AgentData.Version = version;
descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[count];
descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[0];
for (int j = 0; j < count; j++)
descendents.FolderData[j] = folderBlocks[splitPoints[i] + j];
scene.UDP.SendPacket(agent.ID, descendents, PacketCategory.Inventory);
}
}
else
{
Logger.DebugLog("Sending a single InventoryDescendents for folders");
InventoryDescendentsPacket descendents = new InventoryDescendentsPacket();
descendents.AgentData.AgentID = agent.ID;
descendents.AgentData.FolderID = folder.ID;
descendents.AgentData.OwnerID = folder.OwnerID;
descendents.AgentData.Descendents = descendCount;
descendents.AgentData.Version = version;
descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[0];
descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[0];
scene.UDP.SendPacket(agent.ID, descendents, PacketCategory.Inventory);
}
if (itemBlocks.Length > 0)
{
List<int> splitPoints = Helpers.SplitBlocks(itemBlocks, PACKET_OVERHEAD);
Logger.DebugLog(String.Format("Sending {0} InventoryDescendents packets containing {1} items",
splitPoints.Count, itemBlocks.Length));
for (int i = 0; i < splitPoints.Count; i++)
{
int count = (i != splitPoints.Count - 1) ? splitPoints[i + 1] - splitPoints[i] :
itemBlocks.Length - splitPoints[i];
InventoryDescendentsPacket descendents = new InventoryDescendentsPacket();
descendents.AgentData.AgentID = agent.ID;
descendents.AgentData.FolderID = folder.ID;
descendents.AgentData.OwnerID = folder.OwnerID;
descendents.AgentData.Descendents = descendCount;
descendents.AgentData.Version = version;
descendents.FolderData = new InventoryDescendentsPacket.FolderDataBlock[0];
descendents.ItemData = new InventoryDescendentsPacket.ItemDataBlock[count];
for (int j = 0; j < count; j++)
descendents.ItemData[j] = itemBlocks[splitPoints[i] + j];
scene.UDP.SendPacket(agent.ID, descendents, PacketCategory.Inventory);
}
}
}
else
{
Logger.Log("FetchInventoryDescendents called for an unknown folder " + fetch.InventoryData.FolderID,
Helpers.LogLevel.Warning);
}
}
void FetchInventoryHandler(Packet packet, Agent agent)
{
// This is probably too large, but better to be on the safe side
const int PACKET_OVERHEAD = 32;
FetchInventoryPacket fetch = (FetchInventoryPacket)packet;
// Create all of the blocks first. These will be split up into different packets
FetchInventoryReplyPacket.InventoryDataBlock[] blocks =
new FetchInventoryReplyPacket.InventoryDataBlock[fetch.InventoryData.Length];
for (int i = 0; i < fetch.InventoryData.Length; i++)
{
UUID itemID = fetch.InventoryData[i].ItemID;
blocks[i] = new FetchInventoryReplyPacket.InventoryDataBlock();
blocks[i].ItemID = itemID;
InventoryObject obj;
if (scene.Server.Inventory.TryGetInventory(agent.ID, itemID, out obj) && obj is InventoryItem)
{
InventoryItem item = (InventoryItem)obj;
blocks[i].AssetID = item.AssetID;
blocks[i].BaseMask = (uint)item.Permissions.BaseMask;
blocks[i].CRC = item.CRC;
blocks[i].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
blocks[i].CreatorID = item.CreatorID;
blocks[i].Description = Utils.StringToBytes(item.Description);
blocks[i].EveryoneMask = (uint)item.Permissions.EveryoneMask;
blocks[i].Flags = item.Flags;
blocks[i].FolderID = item.ParentID;
blocks[i].GroupID = item.GroupID;
blocks[i].GroupMask = (uint)item.Permissions.GroupMask;
blocks[i].GroupOwned = item.GroupOwned;
blocks[i].InvType = (sbyte)item.InventoryType;
blocks[i].Name = Utils.StringToBytes(item.Name);
blocks[i].NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
blocks[i].OwnerID = item.OwnerID;
blocks[i].OwnerMask = (uint)item.Permissions.OwnerMask;
blocks[i].SalePrice = item.SalePrice;
blocks[i].SaleType = (byte)item.SaleType;
blocks[i].Type = (sbyte)item.AssetType;
}
else
{
Logger.Log("FetchInventory called for an unknown item " + itemID.ToString(),
Helpers.LogLevel.Warning);
blocks[i].Name = Utils.EmptyBytes;
blocks[i].Description = Utils.EmptyBytes;
}
}
// Split the blocks up into multiple packets
List<int> splitPoints = Helpers.SplitBlocks(blocks, PACKET_OVERHEAD);
for (int i = 0; i < splitPoints.Count; i++)
{
int count = (i != splitPoints.Count - 1) ? splitPoints[i + 1] - splitPoints[i] :
blocks.Length - splitPoints[i];
FetchInventoryReplyPacket reply = new FetchInventoryReplyPacket();
reply.AgentData.AgentID = agent.ID;
reply.InventoryData = new FetchInventoryReplyPacket.InventoryDataBlock[count];
for (int j = 0; j < count; j++)
reply.InventoryData[j] = blocks[splitPoints[i] + j];
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Inventory);
}
}
void CopyInventoryItemHandler(Packet packet, Agent agent)
{
CopyInventoryItemPacket copy = (CopyInventoryItemPacket)packet;
for (int i = 0; i < copy.InventoryData.Length; i++)
{
CopyInventoryItemPacket.InventoryDataBlock block = copy.InventoryData[i];
// TODO: This allows someone to copy objects from another
// agent's inventory. Should we allow that, or do any
// permission checks?
// Get the original object
InventoryObject obj;
if (scene.Server.Inventory.TryGetInventory(agent.ID, block.OldItemID, out obj) && obj is InventoryItem)
{
InventoryItem item = (InventoryItem)obj;
// Get the new folder
InventoryObject folderObj;
if (scene.Server.Inventory.TryGetInventory(agent.ID, block.NewFolderID, out folderObj) && folderObj is InventoryFolder)
{
string newName = Utils.BytesToString(block.NewName);
if (String.IsNullOrEmpty(newName))
newName = item.Name;
// Create the copy
InventoryItem newItem = scene.Server.Inventory.CreateItem(agent.ID, newName, item.Description, item.InventoryType,
item.AssetType, item.AssetID, folderObj.ID, item.Permissions.OwnerMask, item.Permissions.NextOwnerMask,
agent.ID, item.CreatorID, UUID.Zero, block.CallbackID);
SendItemCreatedPacket(agent, newItem, UUID.Zero, block.CallbackID);
}
else
{
Logger.Log("CopyInventoryItem called with an unknown target folder " + block.NewFolderID,
Helpers.LogLevel.Warning);
}
}
else
{
Logger.Log("CopyInventoryItem called for an unknown item " + block.OldItemID,
Helpers.LogLevel.Warning);
}
}
}
void MoveInventoryItemHandler(Packet packet, Agent agent)
{
MoveInventoryItemPacket move = (MoveInventoryItemPacket)packet;
// TODO: What is move.AgentData.Stamp for?
List<InventoryObject> objs = new List<InventoryObject>(move.InventoryData.Length);
for (int i = 0; i < move.InventoryData.Length; i++)
{
MoveInventoryItemPacket.InventoryDataBlock block = move.InventoryData[i];
UUID newFolderID = block.FolderID;
if (newFolderID == UUID.Zero)
newFolderID = agent.Info.InventoryRoot;
InventoryObject obj = scene.Server.Inventory.MoveInventory(agent.ID, block.ItemID, newFolderID,
Utils.BytesToString(block.NewName), UUID.Zero, 0);
if (obj != null) objs.Add(obj);
}
SendBulkUpdate(agent, objs, UUID.Zero, 0);
}
void MoveInventoryFolderHandler(Packet packet, Agent agent)
{
MoveInventoryFolderPacket move = (MoveInventoryFolderPacket)packet;
// TODO: What is move.AgentData.Stamp for?
List<InventoryObject> objs = new List<InventoryObject>(move.InventoryData.Length);
for (int i = 0; i < move.InventoryData.Length; i++)
{
MoveInventoryFolderPacket.InventoryDataBlock block = move.InventoryData[i];
UUID newFolderID = block.ParentID;
if (newFolderID == UUID.Zero)
newFolderID = agent.Info.InventoryRoot;
InventoryObject obj = scene.Server.Inventory.MoveInventory(agent.ID, block.FolderID, newFolderID,
null, UUID.Zero, 0);
if (obj != null) objs.Add(obj);
}
SendBulkUpdate(agent, objs, UUID.Zero, 0);
}
void PurgeInventoryDescendentsHandler(Packet packet, Agent agent)
{
PurgeInventoryDescendentsPacket purge = (PurgeInventoryDescendentsPacket)packet;
scene.Server.Inventory.PurgeFolder(agent.ID, purge.InventoryData.FolderID);
}
void DeRezObjectHandler(Packet packet, Agent agent)
{
DeRezObjectPacket derez = (DeRezObjectPacket)packet;
DeRezDestination destination = (DeRezDestination)derez.AgentBlock.Destination;
// TODO: Check permissions
for (int i = 0; i < derez.ObjectData.Length; i++)
{
uint localID = derez.ObjectData[i].ObjectLocalID;
SimulationObject obj;
if (scene.TryGetObject(localID, out obj))
{
switch (destination)
{
case DeRezDestination.AgentInventorySave:
Logger.Log("DeRezObject: Got an AgentInventorySave, DestID: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
break;
case DeRezDestination.AgentInventoryCopy:
Logger.Log("DeRezObject: Got an AgentInventorySave, DestID: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
break;
case DeRezDestination.TaskInventory:
Logger.Log("DeRezObject: Got a TaskInventory, DestID: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
break;
case DeRezDestination.Attachment:
Logger.Log("DeRezObject: Got an Attachment, DestID: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
break;
case DeRezDestination.AgentInventoryTake:
Logger.Log("DeRezObject: Got an AgentInventoryTake, DestID: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
break;
case DeRezDestination.ForceToGodInventory:
Logger.Log("DeRezObject: Got a ForceToGodInventory, DestID: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
break;
case DeRezDestination.TrashFolder:
InventoryObject invObj;
if (scene.Server.Inventory.TryGetInventory(agent.ID, derez.AgentBlock.DestinationID, out invObj) &&
invObj is InventoryFolder)
{
// FIXME: Handle children
InventoryFolder trash = (InventoryFolder)invObj;
InventoryItem item = scene.Server.Inventory.CreateItem(agent.ID, obj.Prim.Properties.Name,
obj.Prim.Properties.Description, InventoryType.Object, AssetType.Object, obj.Prim.ID,
trash.ID, PermissionMask.All, PermissionMask.All, agent.ID, obj.Prim.Properties.CreatorID,
derez.AgentBlock.TransactionID, 0);
scene.ObjectRemove(this, obj.Prim.LocalID);
SendItemCreatedPacket(agent, item, derez.AgentBlock.TransactionID, 0);
Logger.DebugLog(String.Format("Derezzed prim {0} to agent inventory trash", obj.Prim.LocalID));
}
else
{
Logger.Log("DeRezObject: Got a TrashFolder with an invalid trash folder: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
}
break;
case DeRezDestination.AttachmentToInventory:
Logger.Log("DeRezObject: Got an AttachmentToInventory, DestID: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
break;
case DeRezDestination.AttachmentExists:
Logger.Log("DeRezObject: Got an AttachmentExists, DestID: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
break;
case DeRezDestination.ReturnToOwner:
Logger.Log("DeRezObject: Got a ReturnToOwner, DestID: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
break;
case DeRezDestination.ReturnToLastOwner:
Logger.Log("DeRezObject: Got a ReturnToLastOwner, DestID: " +
derez.AgentBlock.DestinationID.ToString(), Helpers.LogLevel.Warning);
break;
}
}
}
}
void SendItemCreatedPacket(Agent agent, InventoryItem item, UUID transactionID, uint callbackID)
{
UpdateCreateInventoryItemPacket update = new UpdateCreateInventoryItemPacket();
update.AgentData.AgentID = agent.ID;
update.AgentData.SimApproved = true;
if (transactionID != UUID.Zero)
update.AgentData.TransactionID = transactionID;
else
update.AgentData.TransactionID = UUID.Random();
update.InventoryData = new UpdateCreateInventoryItemPacket.InventoryDataBlock[1];
update.InventoryData[0] = new UpdateCreateInventoryItemPacket.InventoryDataBlock();
update.InventoryData[0].AssetID = item.AssetID;
update.InventoryData[0].BaseMask = (uint)item.Permissions.BaseMask;
update.InventoryData[0].CallbackID = callbackID;
update.InventoryData[0].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
update.InventoryData[0].CRC = item.CRC;
update.InventoryData[0].CreatorID = item.CreatorID;
update.InventoryData[0].Description = Utils.StringToBytes(item.Description);
update.InventoryData[0].EveryoneMask = (uint)item.Permissions.EveryoneMask;
update.InventoryData[0].Flags = item.Flags;
update.InventoryData[0].FolderID = item.ParentID;
update.InventoryData[0].GroupID = item.GroupID;
update.InventoryData[0].GroupMask = (uint)item.Permissions.GroupMask;
update.InventoryData[0].GroupOwned = item.GroupOwned;
update.InventoryData[0].InvType = (sbyte)item.InventoryType;
update.InventoryData[0].ItemID = item.ID;
update.InventoryData[0].Name = Utils.StringToBytes(item.Name);
update.InventoryData[0].NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
update.InventoryData[0].OwnerID = item.OwnerID;
update.InventoryData[0].OwnerMask = (uint)item.Permissions.OwnerMask;
update.InventoryData[0].SalePrice = item.SalePrice;
update.InventoryData[0].SaleType = (byte)item.SaleType;
update.InventoryData[0].Type = (sbyte)item.AssetType;
scene.UDP.SendPacket(agent.ID, update, PacketCategory.Inventory);
}
void SendBulkUpdate(Agent agent, List<InventoryObject> objs, UUID transactionID, uint callbackID)
{
BulkUpdateInventoryPacket update = new BulkUpdateInventoryPacket();
update.AgentData.AgentID = agent.ID;
update.AgentData.TransactionID = transactionID;
// Count the number of folders and items
int items = 0;
int folders = 0;
for (int i = 0; i < objs.Count; i++)
{
if (objs[i] is InventoryItem)
++items;
else
++folders;
}
update.FolderData = new BulkUpdateInventoryPacket.FolderDataBlock[folders];
update.ItemData = new BulkUpdateInventoryPacket.ItemDataBlock[items];
items = 0;
folders = 0;
for (int i = 0; i < objs.Count; i++)
{
InventoryObject obj = objs[i];
if (obj is InventoryItem)
{
InventoryItem item = (InventoryItem)obj;
update.ItemData[items] = new BulkUpdateInventoryPacket.ItemDataBlock();
update.ItemData[items].AssetID = item.AssetID;
update.ItemData[items].BaseMask = (uint)item.Permissions.BaseMask;
update.ItemData[items].CallbackID = callbackID;
update.ItemData[items].CRC = item.CRC;
update.ItemData[items].CreationDate = (int)Utils.DateTimeToUnixTime(item.CreationDate);
update.ItemData[items].CreatorID = item.CreatorID;
update.ItemData[items].Description = Utils.StringToBytes(item.Description);
update.ItemData[items].EveryoneMask = (uint)item.Permissions.EveryoneMask;
update.ItemData[items].Flags = item.Flags;
update.ItemData[items].FolderID = item.ParentID;
update.ItemData[items].GroupID = item.GroupID;
update.ItemData[items].GroupMask = (uint)item.Permissions.GroupMask;
update.ItemData[items].GroupOwned = item.GroupOwned;
update.ItemData[items].InvType = (sbyte)item.InventoryType;
update.ItemData[items].ItemID = item.ID;
update.ItemData[items].Name = Utils.StringToBytes(item.Name);
update.ItemData[items].NextOwnerMask = (uint)item.Permissions.NextOwnerMask;
update.ItemData[items].OwnerID = item.OwnerID;
update.ItemData[items].OwnerMask = (uint)item.Permissions.OwnerMask;
update.ItemData[items].SalePrice = item.SalePrice;
update.ItemData[items].SaleType = (byte)item.SaleType;
update.ItemData[items].Type = (sbyte)item.InventoryType;
++items;
}
else
{
InventoryFolder folder = (InventoryFolder)obj;
update.FolderData[folders] = new BulkUpdateInventoryPacket.FolderDataBlock();
update.FolderData[folders].FolderID = folder.ID;
update.FolderData[folders].Name = Utils.StringToBytes(folder.Name);
update.FolderData[folders].ParentID = folder.ParentID;
update.FolderData[folders].Type = (sbyte)folder.PreferredType;
++folders;
}
}
Logger.DebugLog("Sending bulk update for " + items + " items and " + folders + " folders");
scene.UDP.SendPacket(agent.ID, update, PacketCategory.Inventory);
}
}
}

View File

@@ -0,0 +1,595 @@
using System;
using System.IO;
using System.Net;
using System.Xml;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenMetaverse.StructuredData;
namespace Simian
{
class HyperGridLink
{
public string RegionName;
public ulong RegionHandle;
public UUID RegionID;
public UUID RegionImage;
public int UDPPort;
public int RemotingPort;
}
public class LLMap : IExtension<ISceneProvider>
{
ISceneProvider scene;
public LLMap()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.MapLayerRequest, MapLayerRequestHandler);
scene.UDP.RegisterPacketCallback(PacketType.MapBlockRequest, MapBlockRequestHandler);
scene.UDP.RegisterPacketCallback(PacketType.TeleportRequest, TeleportRequestHandler);
scene.UDP.RegisterPacketCallback(PacketType.TeleportLocationRequest, TeleportLocationRequestHandler);
return true;
}
public void Stop()
{
}
void MapLayerRequestHandler(Packet packet, Agent agent)
{
MapLayerRequestPacket request = (MapLayerRequestPacket)packet;
GridLayerType type = (GridLayerType)request.AgentData.Flags;
MapLayerReplyPacket reply = new MapLayerReplyPacket();
reply.AgentData.AgentID = agent.ID;
reply.AgentData.Flags = (uint)type;
reply.LayerData = new MapLayerReplyPacket.LayerDataBlock[1];
reply.LayerData[0] = new MapLayerReplyPacket.LayerDataBlock();
reply.LayerData[0].Bottom = 0;
reply.LayerData[0].Left = 0;
reply.LayerData[0].Top = UInt16.MaxValue;
reply.LayerData[0].Right = UInt16.MaxValue;
reply.LayerData[0].ImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f");
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
}
void MapBlockRequestHandler(Packet packet, Agent agent)
{
MapBlockRequestPacket request = (MapBlockRequestPacket)packet;
GridLayerType type = (GridLayerType)request.AgentData.Flags;
MapBlockReplyPacket reply = new MapBlockReplyPacket();
reply.AgentData.AgentID = agent.ID;
reply.AgentData.Flags = (uint)type;
reply.Data = new MapBlockReplyPacket.DataBlock[2];
reply.Data[0] = new MapBlockReplyPacket.DataBlock();
reply.Data[0].Access = (byte)SimAccess.Min;
reply.Data[0].Agents = (byte)scene.AgentCount();
reply.Data[0].MapImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f");
reply.Data[0].Name = Utils.StringToBytes(scene.RegionName);
reply.Data[0].RegionFlags = (uint)scene.RegionFlags;
reply.Data[0].WaterHeight = (byte)scene.WaterHeight;
reply.Data[0].X = (ushort)scene.RegionX;
reply.Data[0].Y = (ushort)scene.RegionY;
reply.Data[1] = new MapBlockReplyPacket.DataBlock();
reply.Data[1].Access = (byte)SimAccess.Min;
reply.Data[1].Agents = 0;
reply.Data[1].MapImageID = new UUID("89556747-24cb-43ed-920b-47caed15465f");
reply.Data[1].Name = Utils.StringToBytes("HyperGrid Portal to OSGrid");
reply.Data[1].RegionFlags = (uint)scene.RegionFlags;
reply.Data[1].WaterHeight = (byte)scene.WaterHeight;
reply.Data[1].X = (ushort)(scene.RegionX + 1);
reply.Data[1].Y = (ushort)scene.RegionY;
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
}
void TeleportRequestHandler(Packet packet, Agent agent)
{
TeleportRequestPacket request = (TeleportRequestPacket)packet;
// TODO: Stand the avatar up first
if (request.Info.RegionID == scene.RegionID)
{
// Local teleport
agent.Avatar.Prim.Position = request.Info.Position;
agent.CurrentLookAt = request.Info.LookAt;
// TODO: Actually adjust the agent's LookAt
TeleportLocalPacket reply = new TeleportLocalPacket();
reply.Info.AgentID = agent.ID;
reply.Info.LocationID = 0; // Unused by the client
reply.Info.LookAt = agent.CurrentLookAt;
reply.Info.Position = agent.Avatar.Prim.Position;
// TODO: Need a "Flying" boolean for Agent
reply.Info.TeleportFlags = (uint)TeleportFlags.ViaRegionID;
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
}
else
{
TeleportFailedPacket reply = new TeleportFailedPacket();
reply.Info.AgentID = agent.ID;
reply.Info.Reason = Utils.StringToBytes("Unknown region");
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
}
}
void TeleportLocationRequestHandler(Packet packet, Agent agent)
{
TeleportLocationRequestPacket request = (TeleportLocationRequestPacket)packet;
// TODO: Stand the avatar up first
if (request.Info.RegionHandle == scene.RegionHandle)
{
// Local teleport
agent.Avatar.Prim.Position = request.Info.Position;
agent.CurrentLookAt = request.Info.LookAt;
TeleportLocalPacket reply = new TeleportLocalPacket();
reply.Info.AgentID = agent.ID;
reply.Info.LocationID = 0; // Unused by the client
reply.Info.LookAt = agent.CurrentLookAt;
reply.Info.Position = agent.Avatar.Prim.Position;
// TODO: Need a "Flying" boolean for Agent
reply.Info.TeleportFlags = (uint)TeleportFlags.ViaLocation;
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
}
else if (request.Info.RegionHandle == Utils.UIntsToLong((scene.RegionX + 1) * 256, scene.RegionY * 256))
{
// Special case: adjacent simulator is the HyperGrid portal
HyperGridTeleport(agent, new Uri("http://osl2.nac.uci.edu:9006/"), request.Info.Position);
}
else
{
TeleportFailedPacket reply = new TeleportFailedPacket();
reply.Info.AgentID = agent.ID;
reply.Info.Reason = Utils.StringToBytes("Unknown region");
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
}
}
bool HyperGridTeleport(Agent agent, Uri destination, Vector3 destPos)
{
HyperGridLink link;
TeleportProgress(agent, "Resolving destination IP address", TeleportFlags.ViaLocation);
IPHostEntry entry = Dns.GetHostEntry(destination.DnsSafeHost);
if (entry.AddressList != null && entry.AddressList.Length >= 1)
{
TeleportProgress(agent, "Retrieving destination details", TeleportFlags.ViaLocation);
if (LinkRegion(destination, out link))
{
TeleportProgress(agent, "Creating foreign agent", TeleportFlags.ViaLocation);
// This is a crufty part of the HyperGrid protocol. We need to generate a fragment of a UUID
// (missing the last four digits) and send that as the caps_path variable. Locally, we expand
// that out to http://foreignsim:httpport/CAPS/fragment0000/ and use it as the seed caps path
// that is sent to the client
UUID seedID = UUID.Random();
string seedCapFragment = seedID.ToString().Substring(0, 32);
Uri seedCap = new Uri(destination, "/CAPS/" + seedCapFragment + "0000/");
if (ExpectHyperGridUser(agent, destination, destPos, link, seedCap))
{
TeleportProgress(agent, "Establishing foreign agent presence", TeleportFlags.ViaLocation);
if (CreateChildAgent(agent, destination, destPos, link, seedCapFragment))
{
// Send the final teleport message to the client
if (scene.HasRunningEventQueue(agent))
{
uint x, y;
Utils.LongToUInts(link.RegionHandle, out x, out y);
x /= 256;
y /= 256;
Logger.Log(String.Format("HyperGrid teleporting to {0} ({1}, {2}) @ {3}",
link.RegionName, x, y, destination), Helpers.LogLevel.Info);
OSDMap info = new OSDMap();
info.Add("AgentID", OSD.FromUUID(agent.ID));
info.Add("LocationID", OSD.FromInteger(4)); // Unused by the client
info.Add("RegionHandle", OSD.FromULong(link.RegionHandle));
info.Add("SeedCapability", OSD.FromUri(seedCap));
info.Add("SimAccess", OSD.FromInteger((byte)SimAccess.Min));
info.Add("SimIP", OSD.FromBinary(entry.AddressList[0].GetAddressBytes()));
info.Add("SimPort", OSD.FromInteger(link.UDPPort));
info.Add("TeleportFlags", OSD.FromUInteger((uint)TeleportFlags.ViaLocation));
OSDArray infoArray = new OSDArray(1);
infoArray.Add(info);
OSDMap teleport = new OSDMap();
teleport.Add("Info", infoArray);
scene.SendEvent(agent, "TeleportFinish", teleport);
}
else
{
Logger.Log("No running EventQueue for " + agent.FullName + ", sending TeleportFinish over UDP",
Helpers.LogLevel.Warning);
TeleportFinishPacket teleport = new TeleportFinishPacket();
teleport.Info.AgentID = agent.ID;
teleport.Info.LocationID = 0; // Unused by the client
teleport.Info.RegionHandle = link.RegionHandle;
teleport.Info.SeedCapability = Utils.StringToBytes(seedCap.ToString());
teleport.Info.SimAccess = (byte)SimAccess.Min;
teleport.Info.SimIP = Utils.BytesToUInt(entry.AddressList[0].GetAddressBytes());
teleport.Info.SimPort = (ushort)link.UDPPort;
teleport.Info.TeleportFlags = (uint)TeleportFlags.ViaLocation;
scene.UDP.SendPacket(agent.ID, teleport, PacketCategory.Transaction);
}
// Remove the agent from the local scene (will also tear down the UDP connection)
//scene.ObjectRemove(this, agent.ID);
return true;
}
}
}
}
return false;
}
bool LinkRegion(Uri destination, out HyperGridLink link)
{
try
{
#region Build Request
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(destination);
request.Method = "POST";
request.ContentType = "text/xml";
MemoryStream memoryStream = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(memoryStream))
{
writer.WriteStartElement("methodCall");
{
writer.WriteElementString("methodName", "link_region");
writer.WriteStartElement("params");
writer.WriteStartElement("param");
writer.WriteStartElement("value");
writer.WriteStartElement("struct");
{
WriteStringMember(writer, "region_name", String.Empty);
}
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.Flush();
}
request.ContentLength = memoryStream.Length;
using (Stream writeStream = request.GetRequestStream())
{
writeStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
#endregion Build Request
#region Parse Response
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
using (XmlReader reader = XmlReader.Create(response.GetResponseStream(), settings))
{
link = new HyperGridLink();
reader.ReadStartElement("methodResponse");
{
reader.ReadStartElement("params");
reader.ReadStartElement("param");
reader.ReadStartElement("value");
reader.ReadStartElement("struct");
{
while (reader.Name == "member")
{
reader.ReadStartElement("member");
{
string name = reader.ReadElementContentAsString("name", String.Empty);
reader.ReadStartElement("value");
{
switch (name)
{
case "region_name":
link.RegionName = reader.ReadElementContentAsString("string", String.Empty);
break;
case "handle":
string handle = reader.ReadElementContentAsString("string", String.Empty);
link.RegionHandle = UInt64.Parse(handle);
break;
case "uuid":
string uuid = reader.ReadElementContentAsString("string", String.Empty);
link.RegionID = UUID.Parse(uuid);
break;
case "internal_port":
link.UDPPort = reader.ReadElementContentAsInt("string", String.Empty);
break;
case "region_image":
string imageuuid = reader.ReadElementContentAsString("string", String.Empty);
link.RegionImage = UUID.Parse(imageuuid);
break;
case "remoting_port":
link.RemotingPort = reader.ReadElementContentAsInt("string", String.Empty);
break;
default:
Logger.Log("[HyperGrid] Unrecognized response XML chunk: " + reader.ReadInnerXml(),
Helpers.LogLevel.Warning);
break;
}
}
reader.ReadEndElement();
}
reader.ReadEndElement();
}
}
reader.ReadEndElement();
reader.ReadEndElement();
reader.ReadEndElement();
reader.ReadEndElement();
}
reader.ReadEndElement();
return true;
}
#endregion Parse Response
}
catch (Exception ex)
{
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
}
link = null;
return false;
}
bool ExpectHyperGridUser(Agent agent, Uri destination, Vector3 destPos, HyperGridLink link, Uri seedCap)
{
try
{
#region Build Request
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(destination);
request.Method = "POST";
request.ContentType = "text/xml";
MemoryStream memoryStream = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(memoryStream))
{
writer.WriteStartElement("methodCall");
{
writer.WriteElementString("methodName", "expect_hg_user");
writer.WriteStartElement("params");
writer.WriteStartElement("param");
writer.WriteStartElement("value");
writer.WriteStartElement("struct");
{
WriteStringMember(writer, "session_id", agent.SessionID.ToString());
WriteStringMember(writer, "secure_session_id", agent.SecureSessionID.ToString());
WriteStringMember(writer, "firstname", agent.Info.FirstName);
WriteStringMember(writer, "lastname", agent.Info.LastName);
WriteStringMember(writer, "agent_id", agent.ID.ToString());
WriteStringMember(writer, "circuit_code", agent.CircuitCode.ToString());
WriteStringMember(writer, "startpos_x", destPos.X.ToString(Utils.EnUsCulture));
WriteStringMember(writer, "startpos_y", destPos.Y.ToString(Utils.EnUsCulture));
WriteStringMember(writer, "startpos_z", destPos.Z.ToString(Utils.EnUsCulture));
WriteStringMember(writer, "caps_path", seedCap.ToString());
WriteStringMember(writer, "region_uuid", link.RegionID.ToString());
//WriteStringMember(writer, "userserver_id", "");
//WriteStringMember(writer, "assetserver_id", "");
//WriteStringMember(writer, "inventoryserver_id", "");
WriteStringMember(writer, "root_folder_id", agent.Info.InventoryRoot.ToString());
string port = scene.Server.HttpUri.Port.ToString();
WriteStringMember(writer, "internal_port", port);
WriteStringMember(writer, "regionhandle", scene.RegionHandle.ToString());
WriteStringMember(writer, "home_address", IPAddress.Loopback.ToString());
WriteStringMember(writer, "home_port", port);
WriteStringMember(writer, "home_remoting", port);
}
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.Flush();
}
request.ContentLength = memoryStream.Length;
using (Stream writeStream = request.GetRequestStream())
{
writeStream.Write(memoryStream.GetBuffer(), 0, (int)memoryStream.Length);
}
#endregion Build Request
#region Parse Response
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreComments = true;
settings.IgnoreWhitespace = true;
using (XmlReader reader = XmlReader.Create(response.GetResponseStream(), settings))
{
bool success = false;
string reason = String.Empty;
reader.ReadStartElement("methodResponse");
{
reader.ReadStartElement("params");
reader.ReadStartElement("param");
reader.ReadStartElement("value");
reader.ReadStartElement("struct");
{
while (reader.Name == "member")
{
reader.ReadStartElement("member");
{
string name = reader.ReadElementContentAsString("name", String.Empty);
reader.ReadStartElement("value");
{
switch (name)
{
case "success":
success = (reader.ReadElementContentAsString("string", String.Empty).ToUpper() == "TRUE");
break;
case "reason":
reason = reader.ReadElementContentAsString("string", String.Empty);
break;
default:
Logger.Log("[HyperGrid] Unrecognized response XML chunk: " + reader.ReadInnerXml(),
Helpers.LogLevel.Warning);
break;
}
}
reader.ReadEndElement();
}
reader.ReadEndElement();
}
}
reader.ReadEndElement();
reader.ReadEndElement();
reader.ReadEndElement();
reader.ReadEndElement();
}
reader.ReadEndElement();
if (!success)
Logger.Log("[HyperGrid] Teleport failed, reason: " + reason, Helpers.LogLevel.Warning);
return success;
}
#endregion Parse Response
}
catch (Exception ex)
{
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
}
return false;
}
bool CreateChildAgent(Agent agent, Uri destination, Vector3 destPos, HyperGridLink link, string seedCapFragment)
{
try
{
destination = new Uri(destination, "/agent/" + agent.ID.ToString() + "/");
OSDMap args = new OSDMap();
args["agent_id"] = OSD.FromUUID(agent.ID);
args["base_folder"] = OSD.FromUUID(UUID.Zero);
args["caps_path"] = OSD.FromString(seedCapFragment);
args["children_seeds"] = OSD.FromBoolean(false);
args["child"] = OSD.FromBoolean(false);
args["circuit_code"] = OSD.FromString(agent.CircuitCode.ToString());
args["first_name"] = OSD.FromString(agent.Info.FirstName);
args["last_name"] = OSD.FromString(agent.Info.LastName);
args["inventory_folder"] = OSD.FromUUID(agent.Info.InventoryRoot);
args["secure_session_id"] = OSD.FromUUID(agent.SecureSessionID);
args["session_id"] = OSD.FromUUID(agent.SessionID);
args["start_pos"] = OSD.FromString(destPos.ToString());
args["destination_handle"] = OSD.FromString(link.RegionHandle.ToString());
LitJson.JsonData jsonData = OSDParser.SerializeJson(args);
byte[] data = System.Text.Encoding.UTF8.GetBytes(jsonData.ToJson());
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(destination);
request.Method = "POST";
request.ContentType = "application/json";
request.ContentLength = data.Length;
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(data, 0, data.Length);
requestStream.Flush();
}
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
bool success = false;
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
Boolean.TryParse(reader.ReadToEnd(), out success);
}
return success;
}
catch (Exception ex)
{
Logger.Log(ex.Message, Helpers.LogLevel.Error, ex);
}
return false;
}
void TeleportProgress(Agent agent, string message, TeleportFlags flags)
{
TeleportProgressPacket progress = new TeleportProgressPacket();
progress.AgentData.AgentID = agent.ID;
progress.Info.Message = Utils.StringToBytes(message);
progress.Info.TeleportFlags = (uint)flags;
scene.UDP.SendPacket(agent.ID, progress, PacketCategory.Transaction);
}
static void WriteStringMember(XmlWriter writer, string name, string value)
{
writer.WriteStartElement("member");
{
writer.WriteElementString("name", name);
writer.WriteStartElement("value");
{
writer.WriteElementString("string", value);
}
writer.WriteEndElement();
}
writer.WriteEndElement();
}
}
}

View File

@@ -0,0 +1,146 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class LLMessaging : IExtension<ISceneProvider>
{
ISceneProvider scene;
public LLMessaging()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.ChatFromViewer, ChatFromViewerHandler);
scene.UDP.RegisterPacketCallback(PacketType.ImprovedInstantMessage, ImprovedInstantMessageHandler);
return true;
}
public void Stop()
{
}
void ChatFromViewerHandler(Packet packet, Agent agent)
{
ChatFromViewerPacket viewerChat = (ChatFromViewerPacket)packet;
scene.ObjectChat(this, agent.ID, agent.ID, ChatAudibleLevel.Fully, (ChatType)viewerChat.ChatData.Type,
ChatSourceType.Agent, agent.FullName, agent.Avatar.GetSimulatorPosition(), viewerChat.ChatData.Channel,
Utils.BytesToString(viewerChat.ChatData.Message));
}
void ImprovedInstantMessageHandler(Packet packet, Agent agent)
{
ImprovedInstantMessagePacket im = (ImprovedInstantMessagePacket)packet;
InstantMessageDialog dialog = (InstantMessageDialog)im.MessageBlock.Dialog;
if (dialog == InstantMessageDialog.MessageFromAgent)
{
// HACK: Only works for agents currently online
Agent recipient;
if (scene.TryGetAgent(im.MessageBlock.ToAgentID, out recipient))
{
// FIXME: Look into the fields we are setting to default values
ImprovedInstantMessagePacket sendIM = new ImprovedInstantMessagePacket();
sendIM.MessageBlock.RegionID = scene.RegionID;
sendIM.MessageBlock.ParentEstateID = 1;
sendIM.MessageBlock.FromGroup = false;
sendIM.MessageBlock.FromAgentName = Utils.StringToBytes(agent.FullName);
sendIM.MessageBlock.ToAgentID = im.MessageBlock.ToAgentID;
sendIM.MessageBlock.Dialog = im.MessageBlock.Dialog;
sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online;
sendIM.MessageBlock.ID = agent.ID;
sendIM.MessageBlock.Message = im.MessageBlock.Message;
sendIM.MessageBlock.BinaryBucket = Utils.EmptyBytes;
sendIM.MessageBlock.Timestamp = Utils.DateTimeToUnixTime(DateTime.Now);
sendIM.MessageBlock.Position = agent.Avatar.GetSimulatorPosition();
sendIM.AgentData.AgentID = agent.ID;
scene.UDP.SendPacket(recipient.ID, sendIM, PacketCategory.Messaging);
}
}
else if (dialog == InstantMessageDialog.FriendshipOffered ||
dialog == InstantMessageDialog.FriendshipAccepted ||
dialog == InstantMessageDialog.FriendshipDeclined)
{
// HACK: Only works for agents currently online
Agent recipient;
if (scene.TryGetAgent(im.MessageBlock.ToAgentID, out recipient))
{
ImprovedInstantMessagePacket sendIM = new ImprovedInstantMessagePacket();
sendIM.MessageBlock.RegionID = scene.RegionID;
sendIM.MessageBlock.ParentEstateID = 1;
sendIM.MessageBlock.FromGroup = false;
sendIM.MessageBlock.FromAgentName = Utils.StringToBytes(agent.FullName);
sendIM.MessageBlock.ToAgentID = im.MessageBlock.ToAgentID;
sendIM.MessageBlock.Dialog = im.MessageBlock.Dialog;
sendIM.MessageBlock.Offline = (byte)InstantMessageOnline.Online;
sendIM.MessageBlock.ID = agent.ID;
sendIM.MessageBlock.Message = im.MessageBlock.Message;
sendIM.MessageBlock.BinaryBucket = Utils.EmptyBytes;
sendIM.MessageBlock.Timestamp = 0;
sendIM.MessageBlock.Position = agent.Avatar.GetSimulatorPosition();
sendIM.AgentData.AgentID = agent.ID;
scene.UDP.SendPacket(recipient.ID, sendIM, PacketCategory.Transaction);
if (dialog == InstantMessageDialog.FriendshipAccepted)
{
bool receiverOnline = scene.ContainsObject(agent.ID);
bool senderOnline = scene.ContainsObject(recipient.ID);
if (receiverOnline)
{
if (senderOnline)
{
OnlineNotificationPacket notify = new OnlineNotificationPacket();
notify.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[0];
notify.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock();
notify.AgentBlock[0].AgentID = agent.ID;
scene.UDP.SendPacket(recipient.ID, notify, PacketCategory.State);
}
else
{
OfflineNotificationPacket notify = new OfflineNotificationPacket();
notify.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[0];
notify.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock();
notify.AgentBlock[0].AgentID = agent.ID;
scene.UDP.SendPacket(recipient.ID, notify, PacketCategory.State);
}
}
if (senderOnline)
{
if (receiverOnline)
{
OnlineNotificationPacket notify = new OnlineNotificationPacket();
notify.AgentBlock = new OnlineNotificationPacket.AgentBlockBlock[0];
notify.AgentBlock[0] = new OnlineNotificationPacket.AgentBlockBlock();
notify.AgentBlock[0].AgentID = recipient.ID;
scene.UDP.SendPacket(agent.ID, notify, PacketCategory.State);
}
else
{
OfflineNotificationPacket notify = new OfflineNotificationPacket();
notify.AgentBlock = new OfflineNotificationPacket.AgentBlockBlock[0];
notify.AgentBlock[0] = new OfflineNotificationPacket.AgentBlockBlock();
notify.AgentBlock[0].AgentID = recipient.ID;
scene.UDP.SendPacket(agent.ID, notify, PacketCategory.State);
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,67 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class LLMoney : IExtension<ISceneProvider>
{
ISceneProvider scene;
public LLMoney()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.MoneyBalanceRequest, MoneyBalanceRequestHandler);
scene.UDP.RegisterPacketCallback(PacketType.MoneyTransferRequest, MoneyTransferRequestHandler);
return true;
}
public void Stop()
{
}
void SendBalance(Agent agent, UUID transactionID, string message)
{
MoneyBalanceReplyPacket reply = new MoneyBalanceReplyPacket();
reply.MoneyData.AgentID = agent.ID;
reply.MoneyData.MoneyBalance = agent.Info.Balance;
reply.MoneyData.TransactionID = transactionID;
reply.MoneyData.Description = Utils.StringToBytes(message);
scene.UDP.SendPacket(agent.ID, reply, PacketCategory.Transaction);
}
void MoneyBalanceRequestHandler(Packet packet, Agent agent)
{
MoneyBalanceRequestPacket request = (MoneyBalanceRequestPacket)packet;
SendBalance(agent, request.MoneyData.TransactionID, String.Empty);
}
void MoneyTransferRequestHandler(Packet packet, Agent agent)
{
MoneyTransferRequestPacket request = (MoneyTransferRequestPacket)packet;
if (request.MoneyData.Amount < 0 || request.MoneyData.Amount > agent.Info.Balance)
return;
// HACK: Only works for sending money to someone who is online
Agent recipient;
if (scene.TryGetAgent(request.MoneyData.DestID, out recipient))
{
agent.Info.Balance -= request.MoneyData.Amount;
recipient.Info.Balance += request.MoneyData.Amount;
SendBalance(agent, UUID.Zero, String.Format("You paid L${0} to {1}.", request.MoneyData.Amount, recipient.FullName));
SendBalance(agent, UUID.Zero, String.Format("{1} paid you L${0}.", request.MoneyData.Amount, agent.FullName));
}
}
}
}

View File

@@ -0,0 +1,518 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
using OpenMetaverse.Rendering;
namespace Simian
{
public class Movement : IExtension<ISceneProvider>
{
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 = .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
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");
static readonly Vector3 SEATING_FUDGE = new Vector3(0.3f, 0.0f, 0.0f);
const float SQRT_TWO = 1.41421356f;
ISceneProvider scene;
Timer updateTimer;
long lastTick;
public int LastTick
{
get { return (int) Interlocked.Read(ref lastTick); }
set { Interlocked.Exchange(ref lastTick, value); }
}
public Movement()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.OnObjectAddOrUpdate += Scene_OnObjectAddOrUpdate;
scene.UDP.RegisterPacketCallback(PacketType.AgentRequestSit, AgentRequestSitHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentSit, AgentSitHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
scene.UDP.RegisterPacketCallback(PacketType.SetAlwaysRun, SetAlwaysRunHandler);
updateTimer = new Timer(new TimerCallback(UpdateTimer_Elapsed));
LastTick = Environment.TickCount;
updateTimer.Change(UPDATE_ITERATION, UPDATE_ITERATION);
return true;
}
public void Stop()
{
if (updateTimer != null)
{
updateTimer.Dispose();
updateTimer = null;
}
}
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;
float seconds = (float)((tick - LastTick) / 1000f);
LastTick = tick;
scene.ForEachAgent(
delegate(Agent agent)
{
if ((agent.Avatar.Prim.Flags & PrimFlags.Physics) == 0)
return;
bool animsChanged = false;
// Create forward and left vectors from the current avatar rotation
Matrix4 rotMatrix = Matrix4.CreateFromQuaternion(agent.Avatar.Prim.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;
Vector3 agentPosition = agent.Avatar.GetSimulatorPosition();
float oldFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
agentPosition += (move * speed);
float newFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
if (!flying && newFloor != oldFloor)
speed /= (1 + (SQRT_TWO * Math.Abs(newFloor - oldFloor)));
//HACK: distance from avatar center to the bottom of its feet
float distanceFromFloor = agent.Avatar.Prim.Scale.Z * .5f;
float lowerLimit = newFloor + distanceFromFloor;
//"bridge" physics
if (agent.Avatar.Prim.Velocity != Vector3.Zero)
{
//start ray at our feet
Vector3 rayStart = new Vector3(
agent.Avatar.Prim.Position.X,
agent.Avatar.Prim.Position.Y,
agent.Avatar.Prim.Position.Z - distanceFromFloor
);
//end ray at 0.01m below our feet
Vector3 rayEnd = new Vector3(
rayStart.X,
rayStart.Y,
rayStart.Z - 0.01f
);
scene.ForEachObject(delegate(SimulationObject obj)
{
//HACK: check nearby objects (what did you expect, octree?)
if (Vector3.Distance(rayStart, obj.Prim.Position) <= 15f)
{
Vector3 collision = scene.Physics.ObjectCollisionTest(rayStart, rayEnd, obj);
if (collision != rayEnd) //we collided!
{
//check if we are any higher than before
float height = collision.Z + distanceFromFloor;
if (height > lowerLimit) lowerLimit = height;
}
}
});
}
// Z acceleration resulting from gravity
float gravity = 0f;
float waterChestHeight = scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f);
if (flying)
{
agent.TickFall = 0;
agent.TickJump = 0;
//velocity falloff while flying
agent.Avatar.Prim.Velocity.X *= 0.66f;
agent.Avatar.Prim.Velocity.Y *= 0.66f;
agent.Avatar.Prim.Velocity.Z *= 0.33f;
if (agent.Avatar.Prim.Position.Z == lowerLimit)
agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE;
if (move.X != 0 || move.Y != 0)
{ //flying horizontally
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLY))
animsChanged = true;
}
else if (move.Z > 0)
{ //flying straight up
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
animsChanged = true;
}
else if (move.Z < 0)
{ //flying straight down
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
animsChanged = true;
}
else
{ //hovering in the air
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
animsChanged = true;
}
}
else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight)
{ //falling, floating, or landing from a jump
if (agent.Avatar.Prim.Position.Z > scene.WaterHeight)
{ //above water
//override controls while drifting
move = Vector3.Zero;
//keep most of our horizontal inertia
agent.Avatar.Prim.Velocity.X *= 0.975f;
agent.Avatar.Prim.Velocity.Y *= 0.975f;
float fallElapsed = (float)(Environment.TickCount - agent.TickFall) / 1000f;
if (agent.TickFall == 0 || (fallElapsed > FALL_DELAY && agent.Avatar.Prim.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 (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
animsChanged = true;
}
}
}
}
else if (agent.Avatar.Prim.Position.Z >= waterChestHeight)
{ //at the water line
gravity = 0f;
agent.Avatar.Prim.Velocity *= 0.5f;
agent.Avatar.Prim.Velocity.Z = 0f;
if (move.Z < 1) agent.Avatar.Prim.Position.Z = waterChestHeight;
if (move.Z > 0)
{
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
animsChanged = true;
}
else if (move.X != 0 || move.Y != 0)
{
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLYSLOW))
animsChanged = true;
}
else
{
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
animsChanged = true;
}
}
else
{ //underwater
gravity = 0f; //buoyant
agent.Avatar.Prim.Velocity *= 0.5f * seconds;
agent.Avatar.Prim.Velocity.Z += 0.75f * seconds;
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
animsChanged = true;
}
}
else
{ //on the ground
agent.TickFall = 0;
//friction
agent.Avatar.Prim.Acceleration *= 0.2f;
agent.Avatar.Prim.Velocity *= 0.2f;
agent.Avatar.Prim.Position.Z = lowerLimit;
if (move.Z > 0)
{ //jumping
if (!jumping)
{ //begin prejump
move.Z = 0; //override Z control
if (scene.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 (scene.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
animsChanged = true;
agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
agent.Avatar.Prim.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 (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
animsChanged = true;
}
else if (agent.Running)
{ //running
if (scene.Avatars.SetDefaultAnimation(agent, Animations.RUN))
animsChanged = true;
}
else
{ //walking
if (scene.Avatars.SetDefaultAnimation(agent, Animations.WALK))
animsChanged = true;
}
}
else
{ //walking
if (move.Z < 0)
{ //crouching
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
animsChanged = true;
}
else
{ //standing
if (scene.Avatars.SetDefaultAnimation(agent, Animations.STAND))
animsChanged = true;
}
}
}
}
if (animsChanged)
scene.Avatars.SendAnimations(agent);
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
// static acceleration when any control is held, otherwise none
if (moving)
{
agent.Avatar.Prim.Acceleration = move * speed;
if (agent.Avatar.Prim.Acceleration.Z < -maxVel)
agent.Avatar.Prim.Acceleration.Z = -maxVel;
else if (agent.Avatar.Prim.Acceleration.Z > maxVel)
agent.Avatar.Prim.Acceleration.Z = maxVel;
}
else agent.Avatar.Prim.Acceleration = Vector3.Zero;
agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity);
if (agent.Avatar.Prim.Velocity.Z < -maxVel)
agent.Avatar.Prim.Velocity.Z = -maxVel;
else if (agent.Avatar.Prim.Velocity.Z > maxVel)
agent.Avatar.Prim.Velocity.Z = maxVel;
agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity;
if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f;
else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f;
if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f;
else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f;
if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.Position.Z = lowerLimit;
}
);
}
void AgentRequestSitHandler(Packet packet, Agent agent)
{
AgentRequestSitPacket request = (AgentRequestSitPacket)packet;
SimulationObject obj;
if (scene.TryGetObject(request.TargetObject.TargetID, out obj))
{
agent.RequestedSitTarget = request.TargetObject.TargetID;
agent.RequestedSitOffset = request.TargetObject.Offset;
AvatarSitResponsePacket response = new AvatarSitResponsePacket();
response.SitObject.ID = request.TargetObject.TargetID;
response.SitTransform.AutoPilot = true;
response.SitTransform.CameraAtOffset = Vector3.Zero;
response.SitTransform.CameraEyeOffset = Vector3.Zero;
response.SitTransform.ForceMouselook = false;
response.SitTransform.SitPosition = request.TargetObject.Offset;
response.SitTransform.SitRotation = obj.SitRotation;
scene.UDP.SendPacket(agent.ID, response, PacketCategory.State);
}
else
{
//TODO: send error
}
}
void AgentSitHandler(Packet packet, Agent agent)
{
AgentSitPacket sit = (AgentSitPacket)packet;
if (agent.RequestedSitTarget != UUID.Zero)
{
SimulationObject obj;
SimulationObject avObj;
if (scene.TryGetObject(agent.RequestedSitTarget, out obj) && scene.TryGetObject(agent.ID, out avObj))
{
agent.Avatar.Prim.Flags &= ~PrimFlags.Physics;
agent.Avatar.Prim.ParentID = obj.Prim.LocalID;
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);
scene.ObjectAddOrUpdate(this, avObj, avObj.Prim.OwnerID, 0, PrimFlags.None,
UpdateFlags.PrimFlags | UpdateFlags.ParentID | UpdateFlags.Position);
scene.Avatars.SetDefaultAnimation(agent, Animations.SIT);
scene.Avatars.SendAnimations(agent);
}
else
{
//TODO: send error
}
agent.RequestedSitTarget = UUID.Zero;
agent.RequestedSitOffset = Vector3.Zero;
}
}
void AgentUpdateHandler(Packet packet, Agent agent)
{
AgentUpdatePacket update = (AgentUpdatePacket)packet;
SimulationObject obj;
if (scene.TryGetObject(agent.ID, 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 (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 = parent.Prim.Position
+ Vector3.Transform(parent.SitPosition, Matrix4.CreateFromQuaternion(parent.SitRotation))
+ Vector3.UnitZ;
agent.Avatar.Prim.ParentID = 0;
scene.Avatars.SetDefaultAnimation(agent, Animations.STAND);
scene.Avatars.SendAnimations(agent);
agent.Avatar.Prim.Flags |= PrimFlags.Physics;
}
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Position | UpdateFlags.Rotation);
}
}
void SetAlwaysRunHandler(Packet packet, Agent agent)
{
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
agent.Running = run.AgentData.AlwaysRun;
}
}
}

View File

@@ -0,0 +1,685 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Rendering;
using OpenMetaverse.Packets;
namespace Simian
{
public class ObjectManager : IExtension<ISceneProvider>
{
ISceneProvider scene;
public ObjectManager()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.ObjectAdd, ObjectAddHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectAttach, ObjectAttachHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectDuplicate, ObjectDuplicateHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectSelect, ObjectSelectHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectDeselect, ObjectDeselectHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectLink, ObjectLinkHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectDelink, ObjectDelinkHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectShape, ObjectShapeHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectFlagUpdate, ObjectFlagUpdateHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectExtraParams, ObjectExtraParamsHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectImage, ObjectImageHandler);
scene.UDP.RegisterPacketCallback(PacketType.Undo, UndoHandler);
scene.UDP.RegisterPacketCallback(PacketType.Redo, RedoHandler);
scene.UDP.RegisterPacketCallback(PacketType.MultipleObjectUpdate, MultipleObjectUpdateHandler);
scene.UDP.RegisterPacketCallback(PacketType.RequestObjectPropertiesFamily, RequestObjectPropertiesFamilyHandler);
return true;
}
public void Stop()
{
}
void ObjectAddHandler(Packet packet, Agent agent)
{
ObjectAddPacket add = (ObjectAddPacket)packet;
Vector3 position = Vector3.Zero;
Vector3 scale = add.ObjectData.Scale;
PCode pcode = (PCode)add.ObjectData.PCode;
PrimFlags flags = (PrimFlags)add.ObjectData.AddFlags;
//bool bypassRaycast = (add.ObjectData.BypassRaycast == 1);
bool rayEndIsIntersection = (add.ObjectData.RayEndIsIntersection == 1);
#region Position Calculation
if (rayEndIsIntersection)
{
// HACK: Blindly trust where the client tells us to place
position = add.ObjectData.RayEnd;
}
else
{
if (add.ObjectData.RayTargetID != UUID.Zero)
{
SimulationObject obj;
if (scene.TryGetObject(add.ObjectData.RayTargetID, out obj))
{
// Test for a collision with the specified object
position = scene.Physics.ObjectCollisionTest(add.ObjectData.RayStart, add.ObjectData.RayEnd, obj);
}
}
if (position == Vector3.Zero)
{
// Test for a collision with the entire scene
position = FullSceneCollisionTest(add.ObjectData.RayStart, add.ObjectData.RayEnd);
}
}
// Position lies on the face of another surface, either terrain of an object.
// Back up along the ray so we are not colliding with the mesh.
// HACK: This is really cheesy and should be done by a collision system
Vector3 rayDir = Vector3.Normalize(add.ObjectData.RayEnd - add.ObjectData.RayStart);
position -= rayDir * scale;
#endregion Position Calculation
#region Foliage Handling
// Set all foliage to phantom
if (pcode == PCode.Grass || pcode == PCode.Tree || pcode == PCode.NewTree)
{
flags |= PrimFlags.Phantom;
if (pcode != PCode.Grass)
{
// Resize based on the foliage type
Tree tree = (Tree)add.ObjectData.State;
switch (tree)
{
case Tree.Cypress1:
case Tree.Cypress2:
scale = new Vector3(4f, 4f, 10f);
break;
default:
scale = new Vector3(4f, 4f, 4f);
break;
}
}
}
#endregion Foliage Handling
// Create an object
Primitive prim = new Primitive();
// TODO: Security check
prim.GroupID = add.AgentData.GroupID;
prim.ID = UUID.Random();
prim.MediaURL = String.Empty;
prim.OwnerID = agent.ID;
prim.Position = position;
prim.PrimData.Material = (Material)add.ObjectData.Material;
prim.PrimData.PathCurve = (PathCurve)add.ObjectData.PathCurve;
prim.PrimData.ProfileCurve = (ProfileCurve)add.ObjectData.ProfileCurve;
prim.PrimData.PathBegin = Primitive.UnpackBeginCut(add.ObjectData.PathBegin);
prim.PrimData.PathEnd = Primitive.UnpackEndCut(add.ObjectData.PathEnd);
prim.PrimData.PathScaleX = Primitive.UnpackPathScale(add.ObjectData.PathScaleX);
prim.PrimData.PathScaleY = Primitive.UnpackPathScale(add.ObjectData.PathScaleY);
prim.PrimData.PathShearX = Primitive.UnpackPathShear((sbyte)add.ObjectData.PathShearX);
prim.PrimData.PathShearY = Primitive.UnpackPathShear((sbyte)add.ObjectData.PathShearY);
prim.PrimData.PathTwist = Primitive.UnpackPathTwist(add.ObjectData.PathTwist);
prim.PrimData.PathTwistBegin = Primitive.UnpackPathTwist(add.ObjectData.PathTwistBegin);
prim.PrimData.PathRadiusOffset = Primitive.UnpackPathTwist(add.ObjectData.PathRadiusOffset);
prim.PrimData.PathTaperX = Primitive.UnpackPathTaper(add.ObjectData.PathTaperX);
prim.PrimData.PathTaperY = Primitive.UnpackPathTaper(add.ObjectData.PathTaperY);
prim.PrimData.PathRevolutions = Primitive.UnpackPathRevolutions(add.ObjectData.PathRevolutions);
prim.PrimData.PathSkew = Primitive.UnpackPathTwist(add.ObjectData.PathSkew);
prim.PrimData.ProfileBegin = Primitive.UnpackBeginCut(add.ObjectData.ProfileBegin);
prim.PrimData.ProfileEnd = Primitive.UnpackEndCut(add.ObjectData.ProfileEnd);
prim.PrimData.ProfileHollow = Primitive.UnpackProfileHollow(add.ObjectData.ProfileHollow);
prim.PrimData.PCode = pcode;
prim.Properties = new Primitive.ObjectProperties();
prim.Properties.CreationDate = DateTime.Now;
prim.Properties.CreatorID = agent.ID;
prim.Properties.Description = String.Empty;
prim.Properties.GroupID = add.AgentData.GroupID;
prim.Properties.LastOwnerID = agent.ID;
prim.Properties.Name = "New Object";
prim.Properties.ObjectID = prim.ID;
prim.Properties.OwnerID = prim.OwnerID;
prim.Properties.Permissions = scene.Server.Permissions.GetDefaultPermissions();
prim.Properties.SalePrice = 10;
prim.RegionHandle = scene.RegionHandle;
prim.Rotation = add.ObjectData.Rotation;
prim.Scale = scale;
prim.TextColor = Color4.Black;
// Add this prim to the object database
SimulationObject simObj = new SimulationObject(prim, scene);
scene.ObjectAddOrUpdate(this, simObj, agent.ID, 0, flags, UpdateFlags.FullUpdate);
}
void ObjectAttachHandler(Packet packet, Agent agent)
{
ObjectAttachPacket attach = (ObjectAttachPacket)packet;
for (int i = 0; i < attach.ObjectData.Length; i++)
{
SimulationObject obj;
if (scene.TryGetObject(attach.ObjectData[i].ObjectLocalID, out obj))
{
obj.BeforeAttachmentRotation = attach.ObjectData[i].Rotation;
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);
// Send an update out to everyone
scene.ObjectAddOrUpdate(this, obj, agent.ID, 0, obj.Prim.Flags,
UpdateFlags.ParentID | UpdateFlags.Position | UpdateFlags.Rotation | UpdateFlags.AttachmentPoint);
}
}
}
void ObjectDuplicateHandler(Packet packet, Agent agent)
{
ObjectDuplicatePacket duplicate = (ObjectDuplicatePacket)packet;
PrimFlags flags = (PrimFlags)duplicate.SharedData.DuplicateFlags;
Vector3 offset = duplicate.SharedData.Offset;
for (int i = 0; i < duplicate.ObjectData.Length; i++)
{
uint dupeID = duplicate.ObjectData[i].ObjectLocalID;
SimulationObject obj;
if (scene.TryGetObject(dupeID, out obj))
{
SimulationObject newObj = new SimulationObject(obj);
newObj.Prim.Position += offset;
newObj.Prim.ID = UUID.Zero;
newObj.Prim.LocalID = 0;
newObj.Prim.Properties.CreationDate = DateTime.Now;
scene.ObjectAddOrUpdate(this, newObj, agent.ID, 0, flags, UpdateFlags.FullUpdate);
}
else
{
Logger.Log("ObjectDuplicate sent for missing object " + dupeID,
Helpers.LogLevel.Warning);
KillObjectPacket kill = new KillObjectPacket();
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
kill.ObjectData[0].ID = dupeID;
scene.UDP.SendPacket(agent.ID, kill, PacketCategory.State);
}
}
}
void ObjectSelectHandler(Packet packet, Agent agent)
{
ObjectSelectPacket select = (ObjectSelectPacket)packet;
for (int i = 0; i < select.ObjectData.Length; i++)
{
ObjectPropertiesPacket properties = new ObjectPropertiesPacket();
properties.ObjectData = new ObjectPropertiesPacket.ObjectDataBlock[1];
properties.ObjectData[0] = new ObjectPropertiesPacket.ObjectDataBlock();
SimulationObject obj;
if (scene.TryGetObject(select.ObjectData[i].ObjectLocalID, out obj))
{
//Logger.DebugLog("Selecting object " + obj.Prim.LocalID);
properties.ObjectData[0].BaseMask = (uint)obj.Prim.Properties.Permissions.BaseMask;
properties.ObjectData[0].CreationDate = Utils.DateTimeToUnixTime(obj.Prim.Properties.CreationDate);
properties.ObjectData[0].CreatorID = obj.Prim.Properties.CreatorID;
properties.ObjectData[0].Description = Utils.StringToBytes(obj.Prim.Properties.Description);
properties.ObjectData[0].EveryoneMask = (uint)obj.Prim.Properties.Permissions.EveryoneMask;
properties.ObjectData[0].GroupID = obj.Prim.Properties.GroupID;
properties.ObjectData[0].GroupMask = (uint)obj.Prim.Properties.Permissions.GroupMask;
properties.ObjectData[0].LastOwnerID = obj.Prim.Properties.LastOwnerID;
properties.ObjectData[0].Name = Utils.StringToBytes(obj.Prim.Properties.Name);
properties.ObjectData[0].NextOwnerMask = (uint)obj.Prim.Properties.Permissions.NextOwnerMask;
properties.ObjectData[0].ObjectID = obj.Prim.ID;
properties.ObjectData[0].OwnerID = obj.Prim.Properties.OwnerID;
properties.ObjectData[0].OwnerMask = (uint)obj.Prim.Properties.Permissions.OwnerMask;
properties.ObjectData[0].OwnershipCost = obj.Prim.Properties.OwnershipCost;
properties.ObjectData[0].SalePrice = obj.Prim.Properties.SalePrice;
properties.ObjectData[0].SaleType = (byte)obj.Prim.Properties.SaleType;
properties.ObjectData[0].SitName = Utils.StringToBytes(obj.Prim.Properties.SitName);
properties.ObjectData[0].TextureID = Utils.EmptyBytes; // FIXME: What is this?
properties.ObjectData[0].TouchName = Utils.StringToBytes(obj.Prim.Properties.TouchName);
scene.UDP.SendPacket(agent.ID, properties, PacketCategory.Transaction);
}
else
{
Logger.Log("ObjectSelect sent for missing object " + select.ObjectData[i].ObjectLocalID,
Helpers.LogLevel.Warning);
properties.ObjectData[0].Description = Utils.EmptyBytes;
properties.ObjectData[0].Name = Utils.EmptyBytes;
properties.ObjectData[0].SitName = Utils.EmptyBytes;
properties.ObjectData[0].TextureID = Utils.EmptyBytes;
properties.ObjectData[0].TouchName = Utils.EmptyBytes;
KillObjectPacket kill = new KillObjectPacket();
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
kill.ObjectData[0].ID = select.ObjectData[i].ObjectLocalID;
scene.UDP.SendPacket(agent.ID, kill, PacketCategory.State);
}
}
}
void ObjectDeselectHandler(Packet packet, Agent agent)
{
ObjectDeselectPacket deselect = (ObjectDeselectPacket)packet;
for (int i = 0; i < deselect.ObjectData.Length; i++)
{
uint localID = deselect.ObjectData[i].ObjectLocalID;
SimulationObject obj;
if (scene.TryGetObject(localID, out obj))
{
//Logger.DebugLog("Deselecting object " + obj.Prim.LocalID);
}
}
// TODO: Do we need this at all?
}
void ObjectLinkHandler(Packet packet, Agent agent)
{
ObjectLinkPacket link = (ObjectLinkPacket)packet;
List<SimulationObject> linkSet = new List<SimulationObject>();
for (int i = 0; i < link.ObjectData.Length; i++)
{
SimulationObject obj;
if (!scene.TryGetObject(link.ObjectData[i].ObjectLocalID, out obj))
{
//TODO: Send an error message
return;
}
else if (obj.Prim.OwnerID != agent.ID)
{
//TODO: Do a full permissions check
return;
}
else
{
linkSet.Add(obj);
}
}
for (int i = 0; i < linkSet.Count; i++)
{
linkSet[i].LinkNumber = i + 1;
if (linkSet[i].Prim.ParentID > 0)
{
// Previously linked children
SimulationObject parent;
if (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));
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));
linkSet[i].Prim.Rotation /= linkSet[0].Prim.Rotation;
// Set parent ID
linkSet[i].Prim.ParentID = linkSet[0].Prim.LocalID;
}
else
{
// Root prim
linkSet[i].Prim.ParentID = 0;
}
scene.ObjectAddOrUpdate(this, linkSet[i], agent.ID, 0, linkSet[i].Prim.Flags,
UpdateFlags.Position | UpdateFlags.Rotation | UpdateFlags.ParentID);
}
}
void ObjectDelinkHandler(Packet packet, Agent agent)
{
ObjectDelinkPacket delink = (ObjectDelinkPacket)packet;
List<SimulationObject> linkSet = new List<SimulationObject>();
for (int i = 0; i < delink.ObjectData.Length; i++)
{
SimulationObject obj;
if (!scene.TryGetObject(delink.ObjectData[i].ObjectLocalID, out obj))
{
//TODO: Send an error message
return;
}
else if (obj.Prim.OwnerID != agent.ID)
{
//TODO: Do a full permissions check
return;
}
else
{
linkSet.Add(obj);
}
}
for (int i = 0; i < linkSet.Count; i++)
{
linkSet[i].Prim.ParentID = 0;
linkSet[i].LinkNumber = 0;
// 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.Rotation *= linkSet[0].Prim.Rotation;
}
scene.ObjectAddOrUpdate(this, linkSet[i], agent.ID, 0, linkSet[i].Prim.Flags,
UpdateFlags.Position | UpdateFlags.Rotation | UpdateFlags.ParentID);
}
}
void ObjectShapeHandler(Packet packet, Agent agent)
{
ObjectShapePacket shape = (ObjectShapePacket)packet;
for (int i = 0; i < shape.ObjectData.Length; i++)
{
ObjectShapePacket.ObjectDataBlock block = shape.ObjectData[i];
SimulationObject obj;
if (scene.TryGetObject(block.ObjectLocalID, out obj))
{
Primitive.ConstructionData data = obj.Prim.PrimData;
data.PathBegin = Primitive.UnpackBeginCut(block.PathBegin);
data.PathCurve = (PathCurve)block.PathCurve;
data.PathEnd = Primitive.UnpackEndCut(block.PathEnd);
data.PathRadiusOffset = Primitive.UnpackPathTwist(block.PathRadiusOffset);
data.PathRevolutions = Primitive.UnpackPathRevolutions(block.PathRevolutions);
data.PathScaleX = Primitive.UnpackPathScale(block.PathScaleX);
data.PathScaleY = Primitive.UnpackPathScale(block.PathScaleY);
data.PathShearX = Primitive.UnpackPathShear((sbyte)block.PathShearX);
data.PathShearY = Primitive.UnpackPathShear((sbyte)block.PathShearY);
data.PathSkew = Primitive.UnpackPathTwist(block.PathSkew);
data.PathTaperX = Primitive.UnpackPathTaper(block.PathTaperX);
data.PathTaperY = Primitive.UnpackPathTaper(block.PathTaperY);
data.PathTwist = Primitive.UnpackPathTwist(block.PathTwist);
data.PathTwistBegin = Primitive.UnpackPathTwist(block.PathTwistBegin);
data.ProfileBegin = Primitive.UnpackBeginCut(block.ProfileBegin);
data.profileCurve = block.ProfileCurve;
data.ProfileEnd = Primitive.UnpackEndCut(block.ProfileEnd);
data.ProfileHollow = Primitive.UnpackProfileHollow(block.ProfileHollow);
obj.Prim.PrimData = data;
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimData);
}
else
{
Logger.Log("Got an ObjectShape packet for unknown object " + block.ObjectLocalID,
Helpers.LogLevel.Warning);
}
}
}
void ObjectFlagUpdateHandler(Packet packet, Agent agent)
{
ObjectFlagUpdatePacket update = (ObjectFlagUpdatePacket)packet;
SimulationObject obj;
if (scene.TryGetObject(update.AgentData.ObjectLocalID, out obj))
{
PrimFlags flags = obj.Prim.Flags;
if (update.AgentData.CastsShadows)
flags |= PrimFlags.CastShadows;
else
flags &= ~PrimFlags.CastShadows;
if (update.AgentData.IsPhantom)
flags |= PrimFlags.Phantom;
else
flags &= ~PrimFlags.Phantom;
if (update.AgentData.IsTemporary)
flags |= PrimFlags.Temporary;
else
flags &= ~PrimFlags.Temporary;
if (update.AgentData.UsePhysics)
flags |= PrimFlags.Physics;
else
flags &= ~PrimFlags.Physics;
obj.Prim.Flags = flags;
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.PrimFlags);
}
else
{
Logger.Log("Got an ObjectFlagUpdate packet for unknown object " + update.AgentData.ObjectLocalID,
Helpers.LogLevel.Warning);
}
}
void ObjectExtraParamsHandler(Packet packet, Agent agent)
{
ObjectExtraParamsPacket extra = (ObjectExtraParamsPacket)packet;
for (int i = 0; i < extra.ObjectData.Length; i++)
{
ObjectExtraParamsPacket.ObjectDataBlock block = extra.ObjectData[i];
SimulationObject obj;
if (scene.TryGetObject(block.ObjectLocalID, out obj))
{
ExtraParamType type = (ExtraParamType)block.ParamType;
if (block.ParamInUse)
{
switch (type)
{
case ExtraParamType.Flexible:
obj.Prim.Flexible = new Primitive.FlexibleData(block.ParamData, 0);
break;
case ExtraParamType.Light:
obj.Prim.Light = new Primitive.LightData(block.ParamData, 0);
break;
case ExtraParamType.Sculpt:
obj.Prim.Sculpt = new Primitive.SculptData(block.ParamData, 0);
break;
}
}
else
{
switch (type)
{
case ExtraParamType.Flexible:
obj.Prim.Flexible = null;
break;
case ExtraParamType.Light:
obj.Prim.Light = null;
break;
case ExtraParamType.Sculpt:
obj.Prim.Sculpt = null;
break;
}
}
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.ExtraData);
}
}
}
void ObjectImageHandler(Packet packet, Agent agent)
{
ObjectImagePacket image = (ObjectImagePacket)packet;
for (int i = 0; i < image.ObjectData.Length; i++)
{
SimulationObject obj;
if (scene.TryGetObject(image.ObjectData[i].ObjectLocalID, out obj))
{
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);
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.MediaURL | UpdateFlags.Textures);
}
}
}
void UndoHandler(Packet packet, Agent agent)
{
UndoPacket undo = (UndoPacket)packet;
for (int i = 0; i < undo.ObjectData.Length; i++)
{
SimulationObject obj;
if (scene.TryGetObject(undo.ObjectData[i].ObjectID, out obj))
scene.ObjectUndo(this, obj);
}
}
void RedoHandler(Packet packet, Agent agent)
{
RedoPacket redo = (RedoPacket)packet;
for (int i = 0; i < redo.ObjectData.Length; i++)
{
SimulationObject obj;
if (scene.TryGetObject(redo.ObjectData[i].ObjectID, out obj))
scene.ObjectRedo(this, obj);
}
}
void MultipleObjectUpdateHandler(Packet packet, Agent agent)
{
MultipleObjectUpdatePacket update = (MultipleObjectUpdatePacket)packet;
for (int i = 0; i < update.ObjectData.Length; i++)
{
bool scaled = false;
MultipleObjectUpdatePacket.ObjectDataBlock block = update.ObjectData[i];
SimulationObject obj;
if (scene.TryGetObject(block.ObjectLocalID, out obj))
{
UpdateType type = (UpdateType)block.Type;
//bool linked = ((type & UpdateType.Linked) != 0);
int pos = 0;
Vector3 position = obj.Prim.Position;
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;
// FIXME: Use this in linksets
//bool uniform = ((type & UpdateType.Uniform) != 0);
}
obj.Prim.Position = position;
obj.Prim.Rotation = rotation;
if (scaled) obj.Prim.Scale = scale;
scene.ObjectAddOrUpdate(this, obj, agent.ID, 0, PrimFlags.None, updateFlags);
}
else
{
// Ghosted prim, send a kill message to this agent
KillObjectPacket kill = new KillObjectPacket();
kill.ObjectData = new KillObjectPacket.ObjectDataBlock[1];
kill.ObjectData[0] = new KillObjectPacket.ObjectDataBlock();
kill.ObjectData[0].ID = block.ObjectLocalID;
scene.UDP.SendPacket(agent.ID, kill, PacketCategory.State);
}
}
}
void RequestObjectPropertiesFamilyHandler(Packet packet, Agent agent)
{
RequestObjectPropertiesFamilyPacket request = (RequestObjectPropertiesFamilyPacket)packet;
ReportType type = (ReportType)request.ObjectData.RequestFlags;
SimulationObject obj;
if (scene.TryGetObject(request.ObjectData.ObjectID, out obj))
{
ObjectPropertiesFamilyPacket props = new ObjectPropertiesFamilyPacket();
props.ObjectData.BaseMask = (uint)obj.Prim.Properties.Permissions.BaseMask;
props.ObjectData.Category = (uint)obj.Prim.Properties.Category;
props.ObjectData.Description = Utils.StringToBytes(obj.Prim.Properties.Description);
props.ObjectData.EveryoneMask = (uint)obj.Prim.Properties.Permissions.EveryoneMask;
props.ObjectData.GroupID = obj.Prim.Properties.GroupID;
props.ObjectData.GroupMask = (uint)obj.Prim.Properties.Permissions.GroupMask;
props.ObjectData.LastOwnerID = obj.Prim.Properties.LastOwnerID;
props.ObjectData.Name = Utils.StringToBytes(obj.Prim.Properties.Name);
props.ObjectData.NextOwnerMask = (uint)obj.Prim.Properties.Permissions.NextOwnerMask;
props.ObjectData.ObjectID = obj.Prim.ID;
props.ObjectData.OwnerID = obj.Prim.Properties.OwnerID;
props.ObjectData.OwnerMask = (uint)obj.Prim.Properties.Permissions.OwnerMask;
props.ObjectData.OwnershipCost = obj.Prim.Properties.OwnershipCost;
props.ObjectData.RequestFlags = (uint)type;
props.ObjectData.SalePrice = obj.Prim.Properties.SalePrice;
props.ObjectData.SaleType = (byte)obj.Prim.Properties.SaleType;
scene.UDP.SendPacket(agent.ID, props, PacketCategory.Transaction);
}
else
{
Logger.Log("RequestObjectPropertiesFamily sent for unknown object " +
request.ObjectData.ObjectID.ToString(), Helpers.LogLevel.Warning);
}
}
Vector3 FullSceneCollisionTest(Vector3 rayStart, Vector3 rayEnd)
{
// HACK: For now
Logger.DebugLog("Full scene collision test was requested, ignoring");
return rayEnd;
}
}
}

View File

@@ -0,0 +1,307 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class ParcelManager : IExtension<ISceneProvider>, IParcelProvider
{
ISceneProvider scene;
Dictionary<int, Parcel> parcels = new Dictionary<int, Parcel>();
/// <summary>X,Y ordered 2D array of the parcelIDs for each sq. meter of a simulator</summary>
int[] parcelOverlay = new int[64 * 64];
public ParcelManager()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
lock (parcels)
parcels.Clear();
// Create a default parcel
Parcel parcel = new Parcel(0);
parcel.Desc = "Simian Parcel";
parcel.Flags = Parcel.ParcelFlags.AllowAPrimitiveEntry | Parcel.ParcelFlags.AllowFly | Parcel.ParcelFlags.AllowGroupScripts |
Parcel.ParcelFlags.AllowLandmark | Parcel.ParcelFlags.AllowOtherScripts | Parcel.ParcelFlags.AllowTerraform |
Parcel.ParcelFlags.AllowVoiceChat | Parcel.ParcelFlags.CreateGroupObjects | Parcel.ParcelFlags.CreateObjects |
Parcel.ParcelFlags.UseBanList;
parcel.Landing = Parcel.LandingType.Direct;
parcel.MaxPrims = 20000;
parcel.Name = "Simian";
parcel.Status = Parcel.ParcelStatus.Leased;
parcel.UserLocation = (parcel.AABBMin + parcel.AABBMax) * 0.5f;
parcel.Bitmap = new byte[512];
for (int i = 0; i < 512; i++)
parcel.Bitmap[i] = Byte.MaxValue;
// The parcelOverlay defaults to all zeroes, and the LocalID of this parcel is zero,
// so we don't need to modify parcelOverlay before calling this
UpdateParcelSize(ref parcel);
// Add the default parcel to the list
parcels[parcel.LocalID] = parcel;
scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesRequest, ParcelPropertiesRequestHandler);
scene.UDP.RegisterPacketCallback(PacketType.ParcelPropertiesUpdate, ParcelPropertiesUpdateHandler);
return true;
}
public void Stop()
{
}
public void SendParcelOverlay(Agent agent)
{
const int LAND_BLOCKS_PER_PACKET = 1024;
byte[] byteArray = new byte[LAND_BLOCKS_PER_PACKET];
int byteArrayCount = 0;
int sequenceID = 0;
for (int y = 0; y < 64; y++)
{
for (int x = 0; x < 64; x++)
{
byte tempByte = 0; // The flags for the current 4x4m parcel square
Parcel parcel;
if (parcels.TryGetValue(parcelOverlay[y * 64 + x], out parcel))
{
// Set the ownership/sale flag
if (parcel.OwnerID == agent.ID)
tempByte = (byte)ParcelOverlayType.OwnedBySelf;
else if (parcel.AuctionID != 0)
tempByte = (byte)ParcelOverlayType.Auction;
else if (parcel.SalePrice > 0 && (parcel.AuthBuyerID == UUID.Zero || parcel.AuthBuyerID == agent.ID))
tempByte = (byte)ParcelOverlayType.ForSale;
else if (parcel.GroupID != UUID.Zero)
tempByte = (byte)ParcelOverlayType.OwnedByGroup;
else if (parcel.OwnerID != UUID.Zero)
tempByte = (byte)ParcelOverlayType.OwnedByOther;
else
tempByte = (byte)ParcelOverlayType.Public;
// Set the border flags
if (x == 0)
tempByte |= (byte)ParcelOverlayType.BorderWest;
else if (parcelOverlay[y * 64 + (x - 1)] != parcel.LocalID)
// Parcel to the west is different from the current parcel
tempByte |= (byte)ParcelOverlayType.BorderWest;
if (y == 0)
tempByte |= (byte)ParcelOverlayType.BorderSouth;
else if (parcelOverlay[(y - 1) * 64 + x] != parcel.LocalID)
// Parcel to the south is different from the current parcel
tempByte |= (byte)ParcelOverlayType.BorderSouth;
byteArray[byteArrayCount] = tempByte;
++byteArrayCount;
if (byteArrayCount >= LAND_BLOCKS_PER_PACKET)
{
// Send a ParcelOverlay packet
ParcelOverlayPacket overlay = new ParcelOverlayPacket();
overlay.ParcelData.SequenceID = sequenceID;
overlay.ParcelData.Data = byteArray;
scene.UDP.SendPacket(agent.ID, overlay, PacketCategory.State);
byteArrayCount = 0;
++sequenceID;
}
}
else
{
Logger.Log("Parcel overlay references missing parcel " + parcelOverlay[y * 64 + x],
Helpers.LogLevel.Warning);
}
}
}
}
void UpdateParcelSize(ref Parcel parcel)
{
int minX = 64;
int minY = 64;
int maxX = 0;
int maxY = 0;
int area = 0;
for (int y = 0; y < 64; y++)
{
for (int x = 0; x < 64; x++)
{
if (parcelOverlay[y * 64 + x] == parcel.LocalID)
{
int x4 = x * 4;
int y4 = y * 4;
if (minX > x4) minX = x4;
if (minY > y4) minY = y4;
if (maxX < x4) maxX = x4;
if (maxX < y4) maxY = y4;
area += 16;
}
}
}
parcel.AABBMin.X = minX;
parcel.AABBMin.Y = minY;
parcel.AABBMax.X = maxX;
parcel.AABBMax.Y = maxY;
parcel.Area = area;
}
void SendParcelProperties(int parcelID, int sequenceID, bool snapSelection, ParcelResult result,
Agent agent)
{
Parcel parcel;
if (parcels.TryGetValue(parcelID, out parcel))
{
ParcelPropertiesPacket properties = new ParcelPropertiesPacket();
properties.AgeVerificationBlock.RegionDenyAgeUnverified = false;
properties.ParcelData.AABBMax = parcel.AABBMax;
properties.ParcelData.AABBMin = parcel.AABBMin;
properties.ParcelData.Area = parcel.Area;
properties.ParcelData.AuctionID = parcel.AuctionID;
properties.ParcelData.AuthBuyerID = parcel.AuthBuyerID;
properties.ParcelData.Bitmap = parcel.Bitmap;
properties.ParcelData.Category = (byte)parcel.Category;
properties.ParcelData.ClaimDate = (int)Utils.DateTimeToUnixTime(parcel.ClaimDate);
properties.ParcelData.ClaimPrice = parcel.ClaimPrice;
properties.ParcelData.Desc = Utils.StringToBytes(parcel.Desc);
properties.ParcelData.GroupID = parcel.GroupID;
properties.ParcelData.GroupPrims = parcel.GroupPrims;
properties.ParcelData.IsGroupOwned = parcel.IsGroupOwned;
properties.ParcelData.LandingType = (byte)parcel.Landing;
properties.ParcelData.LocalID = parcel.LocalID;
properties.ParcelData.MaxPrims = parcel.MaxPrims;
properties.ParcelData.MediaAutoScale = parcel.Media.MediaAutoScale;
properties.ParcelData.MediaID = parcel.Media.MediaID;
properties.ParcelData.MediaURL = Utils.StringToBytes(parcel.Media.MediaURL);
properties.ParcelData.MusicURL = Utils.StringToBytes(parcel.MusicURL);
properties.ParcelData.Name = Utils.StringToBytes(parcel.Name);
properties.ParcelData.OtherCleanTime = parcel.OtherCleanTime;
properties.ParcelData.OtherCount = parcel.OtherCount;
properties.ParcelData.OtherPrims = parcel.OtherPrims;
properties.ParcelData.OwnerID = parcel.OwnerID;
properties.ParcelData.OwnerPrims = parcel.OwnerPrims;
properties.ParcelData.ParcelFlags = (uint)parcel.Flags;
properties.ParcelData.ParcelPrimBonus = parcel.ParcelPrimBonus;
properties.ParcelData.PassHours = parcel.PassHours;
properties.ParcelData.PassPrice = parcel.PassPrice;
properties.ParcelData.PublicCount = parcel.PublicCount;
properties.ParcelData.RegionDenyAnonymous = parcel.RegionDenyAnonymous;
properties.ParcelData.RegionDenyIdentified = false; // Deprecated
properties.ParcelData.RegionDenyTransacted = false; // Deprecated
properties.ParcelData.RegionPushOverride = parcel.RegionPushOverride;
properties.ParcelData.RentPrice = parcel.RentPrice;
properties.ParcelData.RequestResult = (int)result;
properties.ParcelData.SalePrice = parcel.SalePrice;
properties.ParcelData.SelectedPrims = 0; // TODO:
properties.ParcelData.SelfCount = parcel.SelfCount;
properties.ParcelData.SequenceID = sequenceID;
properties.ParcelData.SimWideMaxPrims = parcel.SimWideMaxPrims;
properties.ParcelData.SimWideTotalPrims = parcel.SimWideTotalPrims;
properties.ParcelData.SnapSelection = snapSelection;
properties.ParcelData.SnapshotID = parcel.SnapshotID;
properties.ParcelData.Status = (byte)parcel.Status;
properties.ParcelData.TotalPrims = parcel.TotalPrims;
properties.ParcelData.UserLocation = parcel.UserLocation;
properties.ParcelData.UserLookAt = parcel.UserLookAt;
// HACK: Make everyone think they are the owner of this parcel
properties.ParcelData.OwnerID = agent.ID;
scene.UDP.SendPacket(agent.ID, properties, PacketCategory.Transaction);
}
else
{
Logger.Log("SendParcelProperties() called for unknown parcel " + parcelID, Helpers.LogLevel.Warning);
}
}
void ParcelPropertiesRequestHandler(Packet packet, Agent agent)
{
ParcelPropertiesRequestPacket request = (ParcelPropertiesRequestPacket)packet;
// TODO: Replace with HashSet when we switch to .NET 3.5
List<int> parcels = new List<int>();
// Convert the boundaries to integers
int north = (int)Math.Round(request.ParcelData.North) / 4;
int east = (int)Math.Round(request.ParcelData.East) / 4;
int south = (int)Math.Round(request.ParcelData.South) / 4;
int west = (int)Math.Round(request.ParcelData.West) / 4;
// Find all of the parcels within the given boundaries
int xLen = east - west;
int yLen = north - south;
for (int x = 0; x < xLen; x++)
{
for (int y = 0; y < yLen; y++)
{
if (west + x < 64 && south + y < 64)
{
int currentParcelID = parcelOverlay[(south + y) * 64 + (west + x)];
if (!parcels.Contains(currentParcelID))
parcels.Add(currentParcelID);
}
}
}
ParcelResult result = ParcelResult.NoData;
if (parcels.Count == 1)
result = ParcelResult.Single;
else if (parcels.Count > 1)
result = ParcelResult.Multiple;
for (int i = 0; i < parcels.Count; i++)
SendParcelProperties(parcels[i], request.ParcelData.SequenceID, request.ParcelData.SnapSelection, result, agent);
}
void ParcelPropertiesUpdateHandler(Packet packet, Agent agent)
{
ParcelPropertiesUpdatePacket update = (ParcelPropertiesUpdatePacket)packet;
Parcel parcel;
if (parcels.TryGetValue(update.ParcelData.LocalID, out parcel))
{
parcel.AuthBuyerID = update.ParcelData.AuthBuyerID;
parcel.Category = (Parcel.ParcelCategory)update.ParcelData.Category;
parcel.Desc = Utils.BytesToString(update.ParcelData.Desc);
parcel.Flags = (Parcel.ParcelFlags)update.ParcelData.ParcelFlags;
parcel.GroupID = update.ParcelData.GroupID;
parcel.Landing = (Parcel.LandingType)update.ParcelData.LandingType;
parcel.Media.MediaAutoScale = update.ParcelData.MediaAutoScale;
parcel.Media.MediaID = update.ParcelData.MediaID;
parcel.Media.MediaURL = Utils.BytesToString(update.ParcelData.MediaURL);
parcel.MusicURL = Utils.BytesToString(update.ParcelData.MusicURL);
parcel.Name = Utils.BytesToString(update.ParcelData.Name);
parcel.PassHours = update.ParcelData.PassHours;
parcel.PassPrice = update.ParcelData.PassPrice;
parcel.SalePrice = update.ParcelData.SalePrice;
parcel.SnapshotID = update.ParcelData.SnapshotID;
parcel.UserLocation = update.ParcelData.UserLocation;
parcel.UserLookAt = update.ParcelData.UserLookAt;
lock (parcels)
parcels[parcel.LocalID] = parcel;
if (update.ParcelData.Flags != 0)
SendParcelProperties(parcel.LocalID, 0, false, ParcelResult.Single, agent);
}
else
{
Logger.Log("Got a ParcelPropertiesUpdate for an unknown parcel " + update.ParcelData.LocalID,
Helpers.LogLevel.Warning);
}
}
}
}

View File

@@ -0,0 +1,585 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class Periscope : IExtension<ISceneProvider>
{
// Change this to login to a different grid
const string PERISCOPE_LOGIN_URI = Settings.AGNI_LOGIN_SERVER;
public Agent MasterAgent = null;
ISceneProvider scene;
GridClient client;
PeriscopeImageDelivery imageDelivery;
PeriscopeMovement movement;
PeriscopeTransferManager transferManager;
bool ignoreObjectKill = false;
object loginLock = new object();
string lastPasswordHash = String.Empty;
public Periscope()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
client = new GridClient();
Settings.LOG_LEVEL = Helpers.LogLevel.Info;
client.Settings.MULTIPLE_SIMS = false;
client.Settings.SEND_AGENT_UPDATES = false;
client.Network.OnCurrentSimChanged += Network_OnCurrentSimChanged;
client.Objects.OnNewPrim += Objects_OnNewPrim;
client.Objects.OnNewAvatar += Objects_OnNewAvatar;
client.Objects.OnNewAttachment += Objects_OnNewAttachment;
client.Objects.OnObjectKilled += Objects_OnObjectKilled;
client.Objects.OnObjectUpdated += Objects_OnObjectUpdated;
client.Avatars.OnAvatarAppearance += Avatars_OnAvatarAppearance;
client.Terrain.OnLandPatch += new TerrainManager.LandPatchCallback(Terrain_OnLandPatch);
client.Self.OnChat += new AgentManager.ChatCallback(Self_OnChat);
client.Self.OnTeleport += new AgentManager.TeleportCallback(Self_OnTeleport);
client.Network.RegisterCallback(PacketType.AvatarAnimation, AvatarAnimationHandler);
client.Network.RegisterCallback(PacketType.RegionHandshake, RegionHandshakeHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
scene.UDP.RegisterPacketCallback(PacketType.ChatFromViewer, ChatFromViewerHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectGrab, ObjectGrabHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectGrabUpdate, ObjectGrabUpdateHandler);
scene.UDP.RegisterPacketCallback(PacketType.ObjectDeGrab, ObjectDeGrabHandler);
scene.UDP.RegisterPacketCallback(PacketType.ViewerEffect, ViewerEffectHandler);
scene.UDP.RegisterPacketCallback(PacketType.AgentAnimation, AgentAnimationHandler);
imageDelivery = new PeriscopeImageDelivery(scene, client);
movement = new PeriscopeMovement(scene, this);
transferManager = new PeriscopeTransferManager(scene, client);
return true;
}
public void Stop()
{
transferManager.Stop();
movement.Stop();
imageDelivery.Stop();
if (client.Network.Connected)
client.Network.Logout();
}
void Objects_OnNewPrim(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
{
SimulationObject simObj = new SimulationObject(prim, scene);
if (MasterAgent != null)
simObj.Prim.OwnerID = MasterAgent.ID;
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, scene);
scene.ObjectAddOrUpdate(this, simObj, MasterAgent.ID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
}
void Objects_OnNewAvatar(Simulator simulator, Avatar avatar, ulong regionHandle, ushort timeDilation)
{
// Add the avatar to both the agents list and the scene objects
SimulationObject obj = new SimulationObject(avatar, scene);
Agent agent = new Agent(obj, AgentInfoFromAvatar(avatar));
scene.AgentAdd(this, agent, avatar.Flags);
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.FullUpdate);
}
void Objects_OnObjectUpdated(Simulator simulator, ObjectUpdate update, ulong regionHandle, ushort timeDilation)
{
SimulationObject obj;
UpdateFlags flags = UpdateFlags.Acceleration | UpdateFlags.AngularVelocity | UpdateFlags.Position |
UpdateFlags.Rotation | UpdateFlags.Velocity;
if (update.Avatar) flags |= UpdateFlags.CollisionPlane;
if (update.Textures != null) flags |= UpdateFlags.Textures;
if (scene.TryGetObject(update.LocalID, out obj))
{
obj.Prim.Acceleration = update.Acceleration;
obj.Prim.AngularVelocity = update.AngularVelocity;
obj.Prim.Position = update.Position;
obj.Prim.Rotation = update.Rotation;
obj.Prim.Velocity = update.Velocity;
if (update.Avatar) obj.Prim.CollisionPlane = update.CollisionPlane;
if (update.Textures != null) obj.Prim.Textures = update.Textures;
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, flags);
}
if (update.LocalID == client.Self.LocalID)
{
MasterAgent.Avatar.Prim.Acceleration = update.Acceleration;
MasterAgent.Avatar.Prim.AngularVelocity = update.AngularVelocity;
MasterAgent.Avatar.Prim.Position = update.Position;
MasterAgent.Avatar.Prim.Rotation = update.Rotation;
MasterAgent.Avatar.Prim.Velocity = update.Velocity;
if (update.Avatar) MasterAgent.Avatar.Prim.CollisionPlane = update.CollisionPlane;
if (update.Textures != null) MasterAgent.Avatar.Prim.Textures = update.Textures;
scene.ObjectAddOrUpdate(this, MasterAgent.Avatar, MasterAgent.ID, 0, PrimFlags.None, flags);
}
}
void Objects_OnObjectKilled(Simulator simulator, uint objectID)
{
if (!ignoreObjectKill)
scene.ObjectRemove(this, objectID);
}
void Avatars_OnAvatarAppearance(UUID avatarID, bool isTrial, Primitive.TextureEntryFace defaultTexture,
Primitive.TextureEntryFace[] faceTextures, List<byte> visualParams)
{
Primitive.TextureEntry te = new Primitive.TextureEntry(defaultTexture);
te.FaceTextures = faceTextures;
byte[] vp = (visualParams != null && visualParams.Count > 1 ? visualParams.ToArray() : null);
Agent agent;
if (scene.TryGetAgent(avatarID, out agent))
{
Logger.Log("[Periscope] Updating foreign avatar appearance for " + agent.FullName, Helpers.LogLevel.Info);
scene.AgentAppearance(this, agent, te, vp);
}
if (avatarID == client.Self.AgentID)
{
Logger.Log("[Periscope] Updating foreign avatar appearance for the MasterAgent", Helpers.LogLevel.Info);
scene.AgentAppearance(this, MasterAgent, te, vp);
}
}
void Terrain_OnLandPatch(Simulator simulator, int x, int y, int width, float[] data)
{
float[,] patchData = new float[16, 16];
for (int py = 0; py < 16; py++)
{
for (int px = 0; px < 16; px++)
{
patchData[py, px] = data[py * 16 + px];
}
}
scene.SetTerrainPatch(this, (uint)x, (uint)y, patchData);
}
void Self_OnChat(string message, ChatAudibleLevel audible, ChatType type, ChatSourceType sourceType,
string fromName, UUID id, UUID ownerid, Vector3 position)
{
scene.ObjectChat(this, ownerid, id, audible, type, sourceType, fromName, position, 0, message);
}
void Self_OnTeleport(string message, TeleportStatus status, TeleportFlags flags)
{
if (status == TeleportStatus.Finished)
{
ulong localRegionHandle = scene.RegionHandle;
scene.RemoveAllAgents(
delegate(Agent agent)
{ return agent.Avatar.Prim.RegionHandle != client.Network.CurrentSim.Handle && agent.Avatar.Prim.RegionHandle != localRegionHandle; });
scene.RemoveAllObjects(
delegate(SimulationObject obj)
{ return obj.Prim.RegionHandle != client.Network.CurrentSim.Handle && obj.Prim.RegionHandle != localRegionHandle; });
}
}
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 (scene.TryGetAgent(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);
}
scene.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);
}
scene.Avatars.SendAnimations(MasterAgent);
}
}
void RegionHandshakeHandler(Packet packet, Simulator simulator)
{
RegionHandshakePacket handshake = (RegionHandshakePacket)packet;
handshake.RegionInfo.SimOwner = (MasterAgent != null ? MasterAgent.ID : UUID.Zero);
handshake.RegionInfo.RegionFlags &= ~(uint)RegionFlags.NoFly;
handshake.RegionInfo2.RegionID = scene.RegionID;
// 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
scene.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 = Utils.BytesToString(chat.ChatData.Message);
if (!String.IsNullOrEmpty(message) && message[0] == '/')
{
string[] messageParts = CommandLineParser.Parse(message);
switch (messageParts[0])
{
case "/teleport":
{
float x, y, z;
if (messageParts.Length == 5 &&
Single.TryParse(messageParts[2], out x) &&
Single.TryParse(messageParts[3], out y) &&
Single.TryParse(messageParts[4], out z))
{
scene.Avatars.SendAlert(agent, String.Format("Attempting teleport to {0} <{1}, {2}, {3}>",
messageParts[1], messageParts[2], messageParts[3], messageParts[4]));
client.Self.Teleport(messageParts[1], new Vector3(x, y, z));
}
else
{
scene.Avatars.SendAlert(agent, "Usage: /teleport \"sim name\" x y z");
}
return;
}
case "/stats":
scene.Avatars.SendAlert(agent, String.Format("Downloading textures: {0}, Queued textures: {1}",
imageDelivery.Pipeline.CurrentCount, imageDelivery.Pipeline.QueuedCount));
return;
case "/objectkill":
if (messageParts.Length == 2)
{
if (messageParts[1] == "off" || messageParts[1] == "0")
{
ignoreObjectKill = true;
scene.Avatars.SendAlert(agent, "Ignoring upstream ObjectKill packets");
}
else
{
ignoreObjectKill = false;
scene.Avatars.SendAlert(agent, "Enabling upstream ObjectKill packets");
}
}
return;
case "/save":
{
if (messageParts.Length == 2)
{
string filename = messageParts[1];
string directoryname = Path.GetFileNameWithoutExtension(filename);
Thread saveThread = new Thread(new ThreadStart(
delegate()
{
Logger.Log(String.Format("Preparing to serialize {0} objects", scene.ObjectCount()), Helpers.LogLevel.Info);
OarFile.SavePrims(scene, directoryname + "/objects", directoryname + "/assets", Simian.ASSET_CACHE_DIR);
try { Directory.Delete(directoryname + "/terrains", true); }
catch (Exception) { }
try
{
Directory.CreateDirectory(directoryname + "/terrains");
using (FileStream stream = new FileStream(directoryname + "/terrains/heightmap.r32", FileMode.Create, FileAccess.Write))
{
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
float t = scene.GetTerrainHeightAt(x, y);
stream.Write(BitConverter.GetBytes(t), 0, 4);
}
}
}
}
catch (Exception ex)
{
Logger.Log("Failed saving terrain: " + ex.Message, Helpers.LogLevel.Error);
}
Logger.Log("Saving " + directoryname, Helpers.LogLevel.Info);
OarFile.PackageArchive(directoryname, filename);
try
{ System.IO.Directory.Delete(directoryname, true); }
catch (Exception ex)
{ Logger.Log("Failed to delete temporary directory " + directoryname + ": " + ex.Message, Helpers.LogLevel.Error); }
scene.Avatars.SendAlert(agent, "Finished OAR export to " + filename);
}));
saveThread.Start();
scene.Avatars.SendAlert(agent, "Starting OAR export to " + filename);
}
return;
}
case "/nudemod":
//int count = 0;
// FIXME: AvatarAppearance locks the agents dictionary. Need to be able to copy the agents dictionary?
/*foreach (Agent curAgent in agents.Values)
{
if (curAgent != agent && curAgent.VisualParams != null)
{
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerBaked] = null;
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerJacket] = null;
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerPants] = null;
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerShoes] = null;
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerSocks] = null;
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.LowerUnderpants] = null;
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperBaked] = null;
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperGloves] = null;
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperJacket] = null;
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperShirt] = null;
curAgent.Avatar.Textures.FaceTextures[(int)AppearanceManager.TextureIndex.UpperUndershirt] = null;
scene.AvatarAppearance(this, curAgent, curAgent.Avatar.Textures, curAgent.VisualParams);
++count;
}
}
scene.Avatars.SendAlert(agent, String.Format("Modified appearances for {0} avatar(s)", count));*/
return;
}
}
string finalMessage;
if (agent.Info.FirstName == client.Self.FirstName && agent.Info.LastName == client.Self.LastName)
finalMessage = message;
else
finalMessage = String.Format("<{0} {1}> {2}", agent.Info.FirstName, agent.Info.LastName, message);
client.Self.Chat(finalMessage, chat.ChatData.Channel, (ChatType)chat.ChatData.Type);
}
void AgentUpdateHandler(Packet packet, Agent agent)
{
AgentUpdatePacket update = (AgentUpdatePacket)packet;
if (MasterAgent == null ||
(!client.Network.Connected && client.Network.LoginStatusCode == LoginStatus.Failed && agent.Info.PasswordHash != lastPasswordHash))
{
MasterAgent = null;
// To keep from repeatedly trying to login with a bad password
lastPasswordHash = agent.Info.PasswordHash;
lock (loginLock)
{
// Double-checked locking to avoid hitting the loginLock each time
if (MasterAgent == null && scene.TryGetAgent(update.AgentData.AgentID, out MasterAgent))
{
Logger.Log(String.Format("[Periscope] {0} {1} is the controlling agent",
MasterAgent.Info.FirstName, MasterAgent.Info.LastName), Helpers.LogLevel.Info);
LoginParams login = client.Network.DefaultLoginParams(agent.Info.FirstName, agent.Info.LastName,
agent.Info.PasswordHash, "Simian Periscope", "1.0.0");
login.URI = PERISCOPE_LOGIN_URI;
login.Start = "last";
client.Network.Login(login);
if (client.Network.Connected)
{
Logger.Log("[Periscope] Connected: " + client.Network.LoginMessage, Helpers.LogLevel.Info);
}
else
{
string error = String.Format("Failed to connect to the foreign grid: ({0}): {1}", client.Network.LoginErrorKey,
client.Network.LoginMessage);
Logger.Log("[Periscope] " + error, Helpers.LogLevel.Error);
}
}
}
}
if (MasterAgent == null || update.AgentData.AgentID == MasterAgent.ID)
{
update.AgentData.AgentID = client.Self.AgentID;
update.AgentData.SessionID = client.Self.SessionID;
client.Network.SendPacket(update);
}
}
void ObjectGrabHandler(Packet packet, Agent agent)
{
ObjectGrabPacket grab = (ObjectGrabPacket)packet;
if (MasterAgent == null || grab.AgentData.AgentID == MasterAgent.ID)
{
grab.AgentData.AgentID = client.Self.AgentID;
grab.AgentData.SessionID = client.Self.SessionID;
client.Network.SendPacket(grab);
}
}
void ObjectGrabUpdateHandler(Packet packet, Agent agent)
{
ObjectGrabUpdatePacket grabUpdate = (ObjectGrabUpdatePacket)packet;
if (MasterAgent == null || grabUpdate.AgentData.AgentID == MasterAgent.ID)
{
grabUpdate.AgentData.AgentID = client.Self.AgentID;
grabUpdate.AgentData.SessionID = client.Self.SessionID;
client.Network.SendPacket(grabUpdate);
}
}
void ObjectDeGrabHandler(Packet packet, Agent agent)
{
ObjectDeGrabPacket degrab = (ObjectDeGrabPacket)packet;
if (MasterAgent == null || degrab.AgentData.AgentID == MasterAgent.ID)
{
degrab.AgentData.AgentID = client.Self.AgentID;
degrab.AgentData.SessionID = client.Self.SessionID;
client.Network.SendPacket(degrab);
}
}
void ViewerEffectHandler(Packet packet, Agent agent)
{
ViewerEffectPacket effect = (ViewerEffectPacket)packet;
if (MasterAgent == null || effect.AgentData.AgentID == MasterAgent.ID)
{
effect.AgentData.AgentID = client.Self.AgentID;
effect.AgentData.SessionID = client.Self.SessionID;
client.Network.SendPacket(effect);
}
}
void AgentAnimationHandler(Packet packet, Agent agent)
{
AgentAnimationPacket animation = (AgentAnimationPacket)packet;
if (MasterAgent == null || animation.AgentData.AgentID == MasterAgent.ID)
{
animation.AgentData.AgentID = client.Self.AgentID;
animation.AgentData.SessionID = client.Self.SessionID;
client.Network.SendPacket(animation);
}
}
#endregion Simian client packet handlers
AgentInfo AgentInfoFromAvatar(Avatar avatar)
{
// Some parts of the code use the FirstName/LastName, and other extensions
// might try to access this data. Fill in enough to keep things sane
AgentInfo info = new AgentInfo();
info.AccessLevel = String.Empty;
info.Balance = 0;
info.CreationTime = Utils.DateTimeToUnixTime(Utils.Epoch);
info.FirstName = avatar.FirstName;
info.Height = 1.9f;
info.ID = avatar.ID;
info.LastLoginTime = Utils.DateTimeToUnixTime(DateTime.Now);
info.LastName = avatar.LastName;
return info;
}
static void EraseTexture(Avatar avatar, AppearanceManager.TextureIndex texture)
{
Primitive.TextureEntryFace face = avatar.Textures.FaceTextures[(int)texture];
if (face != null) face.TextureID = UUID.Zero;
}
}
public static class CommandLineParser
{
public static string[] Parse(string str)
{
if (str == null || !(str.Length > 0)) return new string[0];
int idx = str.Trim().IndexOf(" ");
if (idx == -1) return new string[] { str };
int count = str.Length;
List<string> list = new List<string>();
while (count > 0)
{
if (str[0] == '"')
{
int temp = str.IndexOf("\"", 1, str.Length - 1);
while (str[temp - 1] == '\\')
{
temp = str.IndexOf("\"", temp + 1, str.Length - temp - 1);
}
idx = temp + 1;
}
if (str[0] == '\'')
{
int temp = str.IndexOf("\'", 1, str.Length - 1);
while (str[temp - 1] == '\\')
{
temp = str.IndexOf("\'", temp + 1, str.Length - temp - 1);
}
idx = temp + 1;
}
string s = str.Substring(0, idx);
int left = count - idx;
str = str.Substring(idx, left).Trim();
list.Add(s.Trim('"'));
count = str.Length;
idx = str.IndexOf(" ");
if (idx == -1)
{
string add = str.Trim('"', ' ');
if (add.Length > 0)
{
list.Add(add);
}
break;
}
}
return list.ToArray();
}
}
}

View File

@@ -0,0 +1,207 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class PeriscopeImageDelivery
{
public TexturePipeline Pipeline;
ISceneProvider scene;
GridClient client;
Dictionary<UUID, ImageDownload> currentDownloads = new Dictionary<UUID, ImageDownload>();
public PeriscopeImageDelivery(ISceneProvider scene, GridClient client)
{
this.scene = scene;
this.client = client;
Pipeline = new TexturePipeline(client, 12);
Pipeline.OnDownloadFinished += new TexturePipeline.DownloadFinishedCallback(pipeline_OnDownloadFinished);
scene.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, this may be in the pipeline
Pipeline.AbortDownload(block.Image);
}
else
{
//bool bake = ((ImageType)block.Type == ImageType.Baked);
// New download, check if we have this image
Asset asset;
if (scene.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
scene.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;
scene.UDP.SendPacket(download.Agent.ID, 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);
}
scene.UDP.SendPacket(agent.ID, 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);
}
scene.UDP.SendPacket(agent.ID, 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);
}
);
}
}
}
}

View File

@@ -0,0 +1,346 @@
using System;
using System.Collections.Generic;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
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;
ISceneProvider scene;
Periscope periscope;
Timer updateTimer;
long lastTick;
public int LastTick
{
get { return (int)Interlocked.Read(ref lastTick); }
set { Interlocked.Exchange(ref lastTick, value); }
}
public PeriscopeMovement(ISceneProvider scene, Periscope periscope)
{
this.scene = scene;
this.periscope = periscope;
scene.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
scene.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;
scene.ForEachAgent(
delegate(Agent agent)
{
// 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.Prim.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;
Vector3 agentPosition = agent.Avatar.GetSimulatorPosition();
float oldFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
agentPosition += (move * speed);
float newFloor = scene.GetTerrainHeightAt(agentPosition.X, agentPosition.Y);
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.Prim.Scale.Z / 2;
// Z acceleration resulting from gravity
float gravity = 0f;
float waterChestHeight = scene.WaterHeight - (agent.Avatar.Prim.Scale.Z * .33f);
if (flying)
{
agent.TickFall = 0;
agent.TickJump = 0;
//velocity falloff while flying
agent.Avatar.Prim.Velocity.X *= 0.66f;
agent.Avatar.Prim.Velocity.Y *= 0.66f;
agent.Avatar.Prim.Velocity.Z *= 0.33f;
if (agent.Avatar.Prim.Position.Z == lowerLimit)
agent.Avatar.Prim.Velocity.Z += INITIAL_HOVER_IMPULSE;
if (move.X != 0 || move.Y != 0)
{ //flying horizontally
if (scene.Avatars.SetDefaultAnimation(agent, Animations.FLY))
animsChanged = true;
}
else if (move.Z > 0)
{ //flying straight up
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_UP))
animsChanged = true;
}
else if (move.Z < 0)
{ //flying straight down
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER_DOWN))
animsChanged = true;
}
else
{ //hovering in the air
if (scene.Avatars.SetDefaultAnimation(agent, Animations.HOVER))
animsChanged = true;
}
}
else if (agent.Avatar.Prim.Position.Z > lowerLimit + FALL_FORGIVENESS || agent.Avatar.Prim.Position.Z <= waterChestHeight)
{ //falling, floating, or landing from a jump
if (agent.Avatar.Prim.Position.Z > scene.WaterHeight)
{ //above water
move = Vector3.Zero; //override controls while drifting
agent.Avatar.Prim.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.Prim.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 (scene.Avatars.SetDefaultAnimation(agent, Animations.FALLDOWN))
animsChanged = true;
}
}
}
}
}
else
{ //on the ground
agent.TickFall = 0;
//friction
agent.Avatar.Prim.Acceleration *= 0.2f;
agent.Avatar.Prim.Velocity *= 0.2f;
agent.Avatar.Prim.Position.Z = lowerLimit;
if (move.Z > 0)
{ //jumping
if (!jumping)
{ //begin prejump
move.Z = 0; //override Z control
if (scene.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 (scene.Avatars.SetDefaultAnimation(agent, Animations.JUMP))
animsChanged = true;
agent.Avatar.Prim.Velocity.X += agent.Avatar.Prim.Acceleration.X * JUMP_IMPULSE_HORIZONTAL;
agent.Avatar.Prim.Velocity.Y += agent.Avatar.Prim.Acceleration.Y * JUMP_IMPULSE_HORIZONTAL;
agent.Avatar.Prim.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 (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCHWALK))
animsChanged = true;
}
else if (agent.Running)
{ //running
if (scene.Avatars.SetDefaultAnimation(agent, Animations.RUN))
animsChanged = true;
}
else
{ //walking
if (scene.Avatars.SetDefaultAnimation(agent, Animations.WALK))
animsChanged = true;
}
}
else
{ //walking
if (move.Z < 0)
{ //crouching
if (scene.Avatars.SetDefaultAnimation(agent, Animations.CROUCH))
animsChanged = true;
}
else
{ //standing
if (scene.Avatars.SetDefaultAnimation(agent, Animations.STAND))
animsChanged = true;
}
}
}
}
if (animsChanged)
scene.Avatars.SendAnimations(agent);
float maxVel = AVATAR_TERMINAL_VELOCITY * seconds;
// static acceleration when any control is held, otherwise none
if (moving)
{
agent.Avatar.Prim.Acceleration = move * speed;
if (agent.Avatar.Prim.Acceleration.Z < -maxVel)
agent.Avatar.Prim.Acceleration.Z = -maxVel;
else if (agent.Avatar.Prim.Acceleration.Z > maxVel)
agent.Avatar.Prim.Acceleration.Z = maxVel;
}
else
{
agent.Avatar.Prim.Acceleration = Vector3.Zero;
}
agent.Avatar.Prim.Velocity += agent.Avatar.Prim.Acceleration - new Vector3(0f, 0f, gravity);
if (agent.Avatar.Prim.Velocity.Z < -maxVel)
agent.Avatar.Prim.Velocity.Z = -maxVel;
else if (agent.Avatar.Prim.Velocity.Z > maxVel)
agent.Avatar.Prim.Velocity.Z = maxVel;
agent.Avatar.Prim.Position += agent.Avatar.Prim.Velocity;
if (agent.Avatar.Prim.Position.X < 0) agent.Avatar.Prim.Position.X = 0f;
else if (agent.Avatar.Prim.Position.X > 255) agent.Avatar.Prim.Position.X = 255f;
if (agent.Avatar.Prim.Position.Y < 0) agent.Avatar.Prim.Position.Y = 0f;
else if (agent.Avatar.Prim.Position.Y > 255) agent.Avatar.Prim.Position.Y = 255f;
if (agent.Avatar.Prim.Position.Z < lowerLimit) agent.Avatar.Prim.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.Prim.Rotation = update.AgentData.BodyRotation;
agent.ControlFlags = (AgentManager.ControlFlags)update.AgentData.ControlFlags;
agent.State = (AgentState)update.AgentData.State;
agent.HideTitle = update.AgentData.Flags != 0;
}
SimulationObject obj;
if (scene.TryGetObject(update.AgentData.AgentID, out obj))
{
obj.Prim.Rotation = update.AgentData.BodyRotation;
scene.ObjectAddOrUpdate(this, obj, obj.Prim.OwnerID, 0, PrimFlags.None, UpdateFlags.Rotation);
}
}
void SetAlwaysRunHandler(Packet packet, Agent agent)
{
SetAlwaysRunPacket run = (SetAlwaysRunPacket)packet;
agent.Running = run.AgentData.AlwaysRun;
}
}
}

View File

@@ -0,0 +1,407 @@
using System;
using System.Collections.Generic;
using System.IO;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Imaging;
using OpenMetaverse.Packets;
namespace Simian
{
public class PeriscopeTransferManager
{
public const string UPLOAD_DIR = "uploadedAssets";
ISceneProvider scene;
GridClient client;
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
/// <summary>A map from TransactionIDs to AvatarIDs</summary>
Dictionary<UUID, KeyValuePair<UUID, UUID>> currentDownloads = new Dictionary<UUID, KeyValuePair<UUID, UUID>>();
public PeriscopeTransferManager(ISceneProvider scene, GridClient client)
{
this.scene = scene;
this.client = client;
client.Assets.OnAssetReceived += new OpenMetaverse.AssetManager.AssetReceivedCallback(Assets_OnAssetReceived);
scene.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler));
scene.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler));
scene.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler));
scene.UDP.RegisterPacketCallback(PacketType.TransferRequest, new PacketCallback(TransferRequestHandler));
}
public void Stop()
{
}
#region Xfer System
void AssetUploadRequestHandler(Packet packet, Agent agent)
{
AssetUploadRequestPacket request = (AssetUploadRequestPacket)packet;
UUID assetID = UUID.Combine(request.AssetBlock.TransactionID, agent.SecureSessionID);
// Check if the asset is small enough to fit in a single packet
if (request.AssetBlock.AssetData.Length != 0)
{
// Create a new asset from the completed upload
Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, request.AssetBlock.AssetData);
if (asset == null)
{
Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning);
return;
}
Logger.DebugLog(String.Format("Storing uploaded asset {0} ({1})", assetID, asset.AssetType));
asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal);
// Store the asset
scene.Server.Assets.StoreAsset(asset);
// Send a success response
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
complete.AssetBlock.Success = true;
complete.AssetBlock.Type = request.AssetBlock.Type;
complete.AssetBlock.UUID = assetID;
scene.UDP.SendPacket(agent.ID, complete, PacketCategory.Inventory);
}
else
{
// Create a new (empty) asset for the upload
Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, null);
if (asset == null)
{
Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning);
return;
}
Logger.DebugLog(String.Format("Starting upload for {0} ({1})", assetID, asset.AssetType));
asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal);
RequestXferPacket xfer = new RequestXferPacket();
xfer.XferID.DeleteOnCompletion = request.AssetBlock.Tempfile;
xfer.XferID.FilePath = 0;
xfer.XferID.Filename = Utils.EmptyBytes;
xfer.XferID.ID = request.AssetBlock.TransactionID.GetULong();
xfer.XferID.UseBigPackets = false;
xfer.XferID.VFileID = asset.AssetID;
xfer.XferID.VFileType = request.AssetBlock.Type;
// Add this asset to the current upload list
lock (CurrentUploads)
CurrentUploads[xfer.XferID.ID] = asset;
scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Inventory);
}
}
void SendXferPacketHandler(Packet packet, Agent agent)
{
SendXferPacketPacket xfer = (SendXferPacketPacket)packet;
Asset asset;
if (CurrentUploads.TryGetValue(xfer.XferID.ID, out asset))
{
if (asset.AssetData == null)
{
if (xfer.XferID.Packet != 0)
{
Logger.Log(String.Format("Received Xfer packet {0} before the first packet!",
xfer.XferID.Packet), Helpers.LogLevel.Error);
return;
}
uint size = Utils.BytesToUInt(xfer.DataPacket.Data);
asset.AssetData = new byte[size];
Buffer.BlockCopy(xfer.DataPacket.Data, 4, asset.AssetData, 0, xfer.DataPacket.Data.Length - 4);
// Confirm the first upload packet
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
confirm.XferID.ID = xfer.XferID.ID;
confirm.XferID.Packet = xfer.XferID.Packet;
scene.UDP.SendPacket(agent.ID, confirm, PacketCategory.Asset);
}
else
{
Buffer.BlockCopy(xfer.DataPacket.Data, 0, asset.AssetData, (int)xfer.XferID.Packet * 1000,
xfer.DataPacket.Data.Length);
// Confirm this upload packet
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
confirm.XferID.ID = xfer.XferID.ID;
confirm.XferID.Packet = xfer.XferID.Packet;
scene.UDP.SendPacket(agent.ID, confirm, PacketCategory.Asset);
if ((xfer.XferID.Packet & (uint)0x80000000) != 0)
{
// Asset upload finished
Logger.DebugLog(String.Format("Completed Xfer upload of asset {0} ({1}", asset.AssetID, asset.AssetType));
lock (CurrentUploads)
CurrentUploads.Remove(xfer.XferID.ID);
scene.Server.Assets.StoreAsset(asset);
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
complete.AssetBlock.Success = true;
complete.AssetBlock.Type = (sbyte)asset.AssetType;
complete.AssetBlock.UUID = asset.AssetID;
scene.UDP.SendPacket(agent.ID, complete, PacketCategory.Asset);
}
}
}
else
{
Logger.DebugLog("Received a SendXferPacket for an unknown upload");
}
}
void AbortXferHandler(Packet packet, Agent agent)
{
AbortXferPacket abort = (AbortXferPacket)packet;
lock (CurrentUploads)
{
if (CurrentUploads.ContainsKey(abort.XferID.ID))
{
Logger.DebugLog(String.Format("Aborting Xfer {0}, result: {1}", abort.XferID.ID,
(TransferError)abort.XferID.Result));
CurrentUploads.Remove(abort.XferID.ID);
}
else
{
Logger.DebugLog(String.Format("Received an AbortXfer for an unknown xfer {0}",
abort.XferID.ID));
}
}
}
#endregion Xfer System
#region Transfer System
void TransferRequestHandler(Packet packet, Agent agent)
{
TransferRequestPacket request = (TransferRequestPacket)packet;
ChannelType channel = (ChannelType)request.TransferInfo.ChannelType;
SourceType source = (SourceType)request.TransferInfo.SourceType;
if (channel == ChannelType.Asset)
{
if (source == SourceType.Asset)
{
// Parse the request
UUID assetID = new UUID(request.TransferInfo.Params, 0);
AssetType type = (AssetType)(sbyte)Utils.BytesToInt(request.TransferInfo.Params, 16);
// Check if we have this asset
Asset asset;
if (scene.Server.Assets.TryGetAsset(assetID, out asset))
{
if (asset.AssetType == type)
{
TransferToClient(asset, agent, request.TransferInfo.TransferID);
}
else
{
Logger.Log(String.Format(
"Request for asset {0} with type {1} does not match actual asset type {2}",
assetID, type, asset.AssetType), Helpers.LogLevel.Warning);
}
}
else
{
// Use the bot to try and request this asset
lock (currentDownloads)
{
currentDownloads.Add(client.Assets.RequestAsset(assetID, type, false),
new KeyValuePair<UUID, UUID>(agent.ID, request.TransferInfo.TransferID));
}
}
}
else if (source == SourceType.SimEstate)
{
//UUID agentID = new UUID(request.TransferInfo.Params, 0);
//UUID sessionID = new UUID(request.TransferInfo.Params, 16);
//EstateAssetType type = (EstateAssetType)Utils.BytesToInt(request.TransferInfo.Params, 32);
Logger.Log("Please implement estate asset transfers", Helpers.LogLevel.Warning);
}
else if (source == SourceType.SimInventoryItem)
{
//UUID agentID = new UUID(request.TransferInfo.Params, 0);
//UUID sessionID = new UUID(request.TransferInfo.Params, 16);
//UUID ownerID = new UUID(request.TransferInfo.Params, 32);
UUID taskID = new UUID(request.TransferInfo.Params, 48);
//UUID itemID = new UUID(request.TransferInfo.Params, 64);
//UUID assetID = new UUID(request.TransferInfo.Params, 80);
//AssetType type = (AssetType)(sbyte)Utils.BytesToInt(request.TransferInfo.Params, 96);
if (taskID != UUID.Zero)
{
// Task (prim) inventory request
Logger.Log("Please implement task inventory transfers", Helpers.LogLevel.Warning);
}
else
{
// Agent inventory request
Logger.Log("Please implement agent inventory transfer", Helpers.LogLevel.Warning);
}
}
else
{
Logger.Log(String.Format(
"Received a TransferRequest that we don't know how to handle. Channel: {0}, Source: {1}",
channel, source), Helpers.LogLevel.Warning);
}
}
else
{
Logger.Log(String.Format(
"Received a TransferRequest that we don't know how to handle. Channel: {0}, Source: {1}",
channel, source), Helpers.LogLevel.Warning);
}
}
void TransferToClient(Asset asset, Agent agent, UUID transferID)
{
Logger.Log(String.Format("Transferring asset {0} ({1})", asset.AssetID, asset.AssetType), Helpers.LogLevel.Info);
TransferInfoPacket response = new TransferInfoPacket();
response.TransferInfo = new TransferInfoPacket.TransferInfoBlock();
response.TransferInfo.TransferID = transferID;
response.TransferInfo.Params = new byte[20];
Buffer.BlockCopy(asset.AssetID.GetBytes(), 0, response.TransferInfo.Params, 0, 16);
Buffer.BlockCopy(Utils.IntToBytes((int)asset.AssetType), 0, response.TransferInfo.Params, 16, 4);
response.TransferInfo.ChannelType = (int)ChannelType.Asset;
response.TransferInfo.Size = asset.AssetData.Length;
response.TransferInfo.Status = (int)StatusCode.OK;
response.TransferInfo.TargetType = (int)TargetType.Unknown; // Doesn't seem to be used by the client
scene.UDP.SendPacket(agent.ID, response, PacketCategory.Asset);
// Transfer system does not wait for ACKs, just sends all of the
// packets for this transfer out
const int MAX_CHUNK_SIZE = Settings.MAX_PACKET_SIZE - 100;
int processedLength = 0;
int packetNum = 0;
while (processedLength < asset.AssetData.Length)
{
TransferPacketPacket transfer = new TransferPacketPacket();
transfer.TransferData.ChannelType = (int)ChannelType.Asset;
transfer.TransferData.TransferID = transferID;
transfer.TransferData.Packet = packetNum++;
int chunkSize = Math.Min(asset.AssetData.Length - processedLength, MAX_CHUNK_SIZE);
transfer.TransferData.Data = new byte[chunkSize];
Buffer.BlockCopy(asset.AssetData, processedLength, transfer.TransferData.Data, 0, chunkSize);
processedLength += chunkSize;
if (processedLength >= asset.AssetData.Length)
transfer.TransferData.Status = (int)StatusCode.Done;
else
transfer.TransferData.Status = (int)StatusCode.OK;
scene.UDP.SendPacket(agent.ID, transfer, PacketCategory.Asset);
}
}
#endregion Transfer System
void Assets_OnAssetReceived(AssetDownload transfer, Asset asset)
{
KeyValuePair<UUID, UUID> kvp;
Agent agent;
if (currentDownloads.TryGetValue(transfer.ID, out kvp))
{
currentDownloads.Remove(transfer.ID);
if (scene.TryGetAgent(kvp.Key, out agent))
{
if (transfer.Success)
{
scene.Server.Assets.StoreAsset(asset);
TransferToClient(asset, agent, kvp.Value);
}
else
{
Logger.Log("Request for missing asset " + transfer.AssetID.ToString(), Helpers.LogLevel.Warning);
// Asset not found
TransferInfoPacket response = new TransferInfoPacket();
response.TransferInfo = new TransferInfoPacket.TransferInfoBlock();
response.TransferInfo.TransferID = kvp.Value;
response.TransferInfo.Params = new byte[20];
Buffer.BlockCopy(transfer.AssetID.GetBytes(), 0, response.TransferInfo.Params, 0, 16);
Buffer.BlockCopy(Utils.IntToBytes((int)transfer.AssetType), 0, response.TransferInfo.Params, 16, 4);
response.TransferInfo.ChannelType = (int)ChannelType.Asset;
response.TransferInfo.Size = 0;
response.TransferInfo.Status = (int)StatusCode.UnknownSource;
response.TransferInfo.TargetType = (int)TargetType.Unknown;
scene.UDP.SendPacket(agent.ID, response, PacketCategory.Asset);
}
}
else
{
Logger.Log("Asset transfer finished for an untracked agent, ignoring", Helpers.LogLevel.Warning);
}
}
else
{
Logger.Log("Asset transfer finished for an untracked download, ignoring", Helpers.LogLevel.Warning);
}
}
Asset CreateAsset(AssetType type, UUID assetID, byte[] data)
{
switch (type)
{
case AssetType.Bodypart:
return new AssetBodypart(assetID, data);
case AssetType.Clothing:
return new AssetClothing(assetID, data);
case AssetType.LSLBytecode:
return new AssetScriptBinary(assetID, data);
case AssetType.LSLText:
return new AssetScriptText(assetID, data);
case AssetType.Notecard:
return new AssetNotecard(assetID, data);
case AssetType.Texture:
return new AssetTexture(assetID, data);
case AssetType.Animation:
return new AssetAnimation(assetID, data);
case AssetType.CallingCard:
case AssetType.Folder:
case AssetType.Gesture:
case AssetType.ImageJPEG:
case AssetType.ImageTGA:
case AssetType.Landmark:
case AssetType.LostAndFoundFolder:
case AssetType.Object:
case AssetType.RootFolder:
case AssetType.Simstate:
case AssetType.SnapshotFolder:
case AssetType.Sound:
return new AssetSound(assetID, data);
case AssetType.SoundWAV:
case AssetType.TextureTGA:
case AssetType.TrashFolder:
case AssetType.Unknown:
default:
Logger.Log("Asset type " + type.ToString() + " not implemented!", Helpers.LogLevel.Warning);
return null;
}
}
}
}

View File

@@ -0,0 +1,410 @@
using System;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Rendering;
namespace Simian
{
public class PhysicsSimple : IExtension<ISceneProvider>, IPhysicsProvider
{
ISceneProvider scene;
public float TimeDilation
{
get { return 1.0f; }
}
public PhysicsSimple()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
return true;
}
public void Stop()
{
}
public Vector3 ObjectCollisionTest(Vector3 rayStart, Vector3 rayEnd, SimulationObject obj)
{
Vector3 closestPoint = rayEnd;
if (rayStart == rayEnd)
{
Logger.DebugLog("RayStart is equal to RayEnd, returning given location");
return closestPoint;
}
Vector3 direction = Vector3.Normalize(rayEnd - rayStart);
// Get the mesh that has been transformed into world-space
SimpleMesh mesh = obj.GetWorldMesh(DetailLevel.Low, false, false);
if (mesh != null)
{
// Iterate through all of the triangles in the mesh, doing a ray-triangle intersection
float closestDistance = Single.MaxValue;
for (int i = 0; i < mesh.Indices.Count; i += 3)
{
Vector3 point0 = mesh.Vertices[mesh.Indices[i + 0]].Position;
Vector3 point1 = mesh.Vertices[mesh.Indices[i + 1]].Position;
Vector3 point2 = mesh.Vertices[mesh.Indices[i + 2]].Position;
Vector3 collisionPoint;
if (RayTriangleIntersection(rayStart, direction, point0, point1, point2, out collisionPoint))
{
if ((collisionPoint - rayStart).Length() < closestDistance)
closestPoint = collisionPoint;
}
}
}
return closestPoint;
}
public bool TryGetObjectMass(UUID objectID, out float mass)
{
SimulationObject obj;
if (scene.TryGetObject(objectID, out obj))
{
mass = CalculateMass(obj.Prim);
return true;
}
else
{
mass = 0f;
return false;
}
}
/// <summary>
/// Adapted from http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf
/// </summary>
/// <param name="origin">Origin point of the ray</param>
/// <param name="direction">Unit vector representing the direction of the ray</param>
/// <param name="vert0">Position of the first triangle corner</param>
/// <param name="vert1">Position of the second triangle corner</param>
/// <param name="vert2">Position of the third triangle corner</param>
/// <param name="collisionPoint">The collision point in the triangle</param>
/// <returns>True if the ray passes through the triangle, otherwise false</returns>
static bool RayTriangleIntersection(Vector3 origin, Vector3 direction, Vector3 vert0, Vector3 vert1, Vector3 vert2, out Vector3 collisionPoint)
{
const float EPSILON = 0.00001f;
Vector3 edge1, edge2, pvec;
float determinant, invDeterminant;
// Find vectors for two edges sharing vert0
edge1 = vert1 - vert0;
edge2 = vert2 - vert0;
// Begin calculating the determinant
pvec = Vector3.Cross(direction, edge2);
// If the determinant is near zero, ray lies in plane of triangle
determinant = Vector3.Dot(edge1, pvec);
if (determinant > -EPSILON && determinant < EPSILON)
{
collisionPoint = Vector3.Zero;
return false;
}
invDeterminant = 1f / determinant;
// Calculate distance from vert0 to ray origin
Vector3 tvec = origin - vert0;
// Calculate U parameter and test bounds
float u = Vector3.Dot(tvec, pvec) * invDeterminant;
if (u < 0.0f || u > 1.0f)
{
collisionPoint = Vector3.Zero;
return false;
}
// Prepare to test V parameter
Vector3 qvec = Vector3.Cross(tvec, edge1);
// Calculate V parameter and test bounds
float v = Vector3.Dot(direction, qvec) * invDeterminant;
if (v < 0.0f || u + v > 1.0f)
{
collisionPoint = Vector3.Zero;
return false;
}
//t = Vector3.Dot(edge2, qvec) * invDeterminant;
collisionPoint = new Vector3(
vert0.X + u * (vert1.X - vert0.X) + v * (vert2.X - vert0.X),
vert0.Y + u * (vert1.Y - vert0.Y) + v * (vert2.Y - vert0.Y),
vert0.Z + u * (vert1.Z - vert0.Z) + v * (vert2.Z - vert0.Z));
return true;
}
/// <summary>
/// Adapted from code written by Teravus for OpenSim
/// </summary>
/// <param name="prim">Primitive to calculate the mass of</param>
/// <returns>Estimated mass of the given primitive</returns>
static float CalculateMass(Primitive prim)
{
const float PRIM_DENSITY = 10.000006836f; // Aluminum g/cm3
float volume = 0f;
float returnMass = 0f;
// TODO: Use the prim material in mass calculations once our physics
// engine supports different materials
switch (prim.PrimData.ProfileCurve)
{
case ProfileCurve.Square:
// Profile Volume
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
// If the user has 'hollowed out'
if (prim.PrimData.ProfileHollow > 0.0f)
{
float hollowAmount = prim.PrimData.ProfileHollow;
// calculate the hollow volume by it's shape compared to the prim shape
float hollowVolume = 0;
switch (prim.PrimData.ProfileHole)
{
case HoleType.Square:
case HoleType.Same:
// Cube Hollow volume calculation
float hollowsizex = prim.Scale.X * hollowAmount;
float hollowsizey = prim.Scale.Y * hollowAmount;
float hollowsizez = prim.Scale.Z * hollowAmount;
hollowVolume = hollowsizex * hollowsizey * hollowsizez;
break;
case HoleType.Circle:
// Hollow shape is a perfect cyllinder in respect to the cube's scale
// Cyllinder hollow volume calculation
float hRadius = prim.Scale.X * 0.5f;
float hLength = prim.Scale.Z;
// pi * r2 * h
hollowVolume = ((float)(Math.PI * Math.Pow(hRadius, 2) * hLength) * hollowAmount);
break;
case HoleType.Triangle:
// Equilateral Triangular Prism volume hollow calculation
// Triangle is an Equilateral Triangular Prism with aLength = to _size.Y
float aLength = prim.Scale.Y;
// 1/2 abh
hollowVolume = (float)((0.5 * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount);
break;
default:
hollowVolume = 0;
break;
}
volume = volume - hollowVolume;
}
break;
case ProfileCurve.Circle:
if (prim.PrimData.PathCurve == PathCurve.Line)
{
// Cylinder
float volume1 = (float)(Math.PI * Math.Pow(prim.Scale.X / 2, 2) * prim.Scale.Z);
float volume2 = (float)(Math.PI * Math.Pow(prim.Scale.Y / 2, 2) * prim.Scale.Z);
// Approximating the cylinder's irregularity.
if (volume1 > volume2)
{
volume = (float)volume1 - (volume1 - volume2);
}
else if (volume2 > volume1)
{
volume = (float)volume2 - (volume2 - volume1);
}
else
{
// Regular cylinder
volume = volume1;
}
}
else
{
// We don't know what the shape is yet, so use default
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
}
// If the user has 'hollowed out'
if (prim.PrimData.ProfileHollow > 0.0f)
{
float hollowAmount = prim.PrimData.ProfileHollow;
// calculate the hollow volume by it's shape compared to the prim shape
float hollowVolume = 0f;
switch (prim.PrimData.ProfileHole)
{
case HoleType.Circle:
case HoleType.Same:
// Hollow shape is a perfect cyllinder in respect to the cube's scale
// Cyllinder hollow volume calculation
float hRadius = prim.Scale.X * 0.5f;
float hLength = prim.Scale.Z;
// pi * r2 * h
hollowVolume = ((float)(Math.PI * Math.Pow(hRadius, 2) * hLength) * hollowAmount);
break;
case HoleType.Square:
// Cube Hollow volume calculation
float hollowsizex = prim.Scale.X * hollowAmount;
float hollowsizey = prim.Scale.Y * hollowAmount;
float hollowsizez = prim.Scale.Z * hollowAmount;
hollowVolume = hollowsizex * hollowsizey * hollowsizez;
break;
case HoleType.Triangle:
// Equilateral Triangular Prism volume hollow calculation
// Triangle is an Equilateral Triangular Prism with aLength = to _size.Y
float aLength = prim.Scale.Y;
// 1/2 abh
hollowVolume = (0.5f * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount;
break;
default:
hollowVolume = 0;
break;
}
volume = volume - hollowVolume;
}
break;
case ProfileCurve.HalfCircle:
if (prim.PrimData.PathCurve == PathCurve.Circle)
{
if (prim.Scale.X == prim.Scale.Y && prim.Scale.Y == prim.Scale.Z)
{
// regular sphere
// v = 4/3 * pi * r^3
float sradius3 = (float)Math.Pow((prim.Scale.X * 0.5f), 3);
volume = (4f / 3f) * (float)Math.PI * sradius3;
}
else
{
// we treat this as a box currently
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
}
}
else
{
// We don't know what the shape is yet, so use default
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
}
break;
case ProfileCurve.EqualTriangle:
float xA = -0.25f * prim.Scale.X;
float yA = -0.45f * prim.Scale.Y;
float xB = 0.5f * prim.Scale.X;
float yB = 0;
float xC = -0.25f * prim.Scale.X;
float yC = 0.45f * prim.Scale.Y;
volume = (float)((Math.Abs((xB * yA - xA * yB) + (xC * yB - xB * yC) + (xA * yC - xC * yA)) / 2) * prim.Scale.Z);
// If the user has 'hollowed out'
// ProfileHollow is one of those 0 to 50000 values :P
// we like percentages better.. so turning into a percentage
if (prim.PrimData.ProfileHollow > 0.0f)
{
float hollowAmount = prim.PrimData.ProfileHollow;
// calculate the hollow volume by it's shape compared to the prim shape
float hollowVolume = 0f;
switch (prim.PrimData.ProfileHole)
{
case HoleType.Triangle:
case HoleType.Same:
// Equilateral Triangular Prism volume hollow calculation
// Triangle is an Equilateral Triangular Prism with aLength = to _size.Y
float aLength = prim.Scale.Y;
// 1/2 abh
hollowVolume = (0.5f * aLength * prim.Scale.X * prim.Scale.Z) * hollowAmount;
break;
case HoleType.Square:
// Cube Hollow volume calculation
float hollowsizex = prim.Scale.X * hollowAmount;
float hollowsizey = prim.Scale.Y * hollowAmount;
float hollowsizez = prim.Scale.Z * hollowAmount;
hollowVolume = hollowsizex * hollowsizey * hollowsizez;
break;
case HoleType.Circle:
// Hollow shape is a perfect cyllinder in respect to the cube's scale
// Cyllinder hollow volume calculation
float hRadius = prim.Scale.X * 0.5f;
float hLength = prim.Scale.Z;
// pi * r2 * h
hollowVolume = ((float)((Math.PI * Math.Pow(hRadius, 2) * hLength) / 2) * hollowAmount);
break;
default:
hollowVolume = 0;
break;
}
volume = volume - hollowVolume;
}
break;
default:
// we don't have all of the volume formulas yet so
// use the common volume formula for all
volume = prim.Scale.X * prim.Scale.Y * prim.Scale.Z;
break;
}
// Calculate Path cut effect on volume
// Not exact, in the triangle hollow example
// They should never be zero or less then zero..
// we'll ignore it if it's less then zero
if (prim.PrimData.ProfileBegin + prim.PrimData.ProfileEnd > 0.0f)
{
float pathCutAmount = prim.PrimData.ProfileBegin + prim.PrimData.ProfileEnd;
// Check the return amount for sanity
if (pathCutAmount >= 0.99f)
pathCutAmount = 0.99f;
volume = volume - (volume * pathCutAmount);
}
// Mass = density * volume
if (prim.PrimData.PathTaperX != 1f)
volume *= (prim.PrimData.PathTaperX / 3f) + 0.001f;
if (prim.PrimData.PathTaperY != 1f)
volume *= (prim.PrimData.PathTaperY / 3f) + 0.001f;
returnMass = PRIM_DENSITY * volume;
if (returnMass <= 0f)
returnMass = 0.0001f; //ckrinke: Mass must be greater then zero.
return returnMass;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,229 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using ExtensionLoader;
using OpenMetaverse;
namespace Simian
{
public class ScriptFunction
{
public MethodInfo Method;
public string Name;
public List<Type> Parameters;
public Type Return;
}
public class ScriptConsole : IExtension<ISceneProvider>
{
ISceneProvider scene;
IScriptApi api;
Dictionary<string, ScriptFunction> functions;
public ScriptConsole()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
// Create a single local scripting API instance that we will manage
api = new ScriptApi();
api.Start(scene, null, UUID.Zero, false, false);
// Create a dictionary of all of the scripting functions
functions = new Dictionary<string, ScriptFunction>();
Type apiType = typeof(IScriptApi);
MethodInfo[] apiMethods = apiType.GetMethods();
for (int i = 0; i < apiMethods.Length; i++)
{
MethodInfo method = apiMethods[i];
if (!method.IsConstructor && method.Name != "Start" && method.Name != "Stop")
{
ScriptFunction function = new ScriptFunction();
function.Method = method;
function.Name = method.Name;
function.Return = method.ReturnParameter.ParameterType.UnderlyingSystemType;
function.Parameters = new List<Type>();
ParameterInfo[] parms = method.GetParameters();
for (int j = 0; j < parms.Length; j++)
function.Parameters.Add(parms[j].ParameterType.UnderlyingSystemType);
functions.Add(function.Name, function);
}
}
scene.OnObjectChat += Scene_OnObjectChat;
return true;
}
public void Stop()
{
lock (api)
api.Stop();
}
void PrintFunctionUsage(ScriptFunction function)
{
string message = "Usage: " + function.Return.Name + " " + function.Name + "(";
for (int i = 0; i < function.Parameters.Count; i++)
{
message += function.Parameters[i].Name;
if (i != function.Parameters.Count - 1)
message += ", ";
}
message += ")";
scene.ObjectChat(this, UUID.Zero, UUID.Zero, ChatAudibleLevel.Fully, ChatType.OwnerSay, ChatSourceType.Object,
"Script Console", Vector3.Zero, 0, message);
}
List<string> ParseParameters(string paramsString)
{
List<string> parameters = new List<string>();
int i = 0;
while (i < paramsString.Length)
{
int commaPos = paramsString.IndexOf(',', i);
int bracketPos = paramsString.IndexOf('<', i);
int endBracketPos = paramsString.IndexOf('>', i);
string param;
if (bracketPos > 0 && bracketPos < commaPos)
{
if (endBracketPos > bracketPos)
commaPos = paramsString.IndexOf(',', endBracketPos);
else
return null;
}
if (commaPos > 0)
{
// ...,
param = paramsString.Substring(i, commaPos - i);
i = commaPos + 1;
}
else
{
// ...
param = paramsString.Substring(i);
i = paramsString.Length;
}
parameters.Add(param);
}
return parameters;
}
object[] ConvertParameters(ScriptFunction function, List<string> parameters)
{
object[] objParameters = new object[function.Parameters.Count];
for (int i = 0; i < objParameters.Length; i++)
{
Type paramType = function.Parameters[i];
if (paramType == typeof(int))
{
int value;
if (Int32.TryParse(parameters[i], out value))
objParameters[i] = value;
else
return null;
}
else if (paramType == typeof(double))
{
double value;
if (Double.TryParse(parameters[i], out value))
objParameters[i] = value;
else
return null;
}
else if (paramType == typeof(ScriptTypes.LSL_Vector))
{
ScriptTypes.LSL_Vector value;
value = (ScriptTypes.LSL_Vector)parameters[i];
objParameters[i] = value;
}
else if (paramType == typeof(ScriptTypes.LSL_Rotation))
{
ScriptTypes.LSL_Rotation value;
value = (ScriptTypes.LSL_Rotation)parameters[i];
objParameters[i] = value;
}
else
{
// String value, or something that can (hopefully) be implicitly converted
objParameters[i] = parameters[i];
}
}
return objParameters;
}
void Scene_OnObjectChat(object sender, UUID ownerID, UUID sourceID, ChatAudibleLevel audible, ChatType type,
ChatSourceType sourceType, string fromName, Vector3 position, int channel, string message)
{
if (sourceType == ChatSourceType.Agent && type == ChatType.Normal)
{
if (message.StartsWith("ll") && message.Contains("("))
{
int startParam = message.IndexOf('(');
int endParam = message.IndexOf(')');
if (startParam > 2 && endParam > startParam)
{
// Try and parse this into a function call
string name = message.Substring(0, startParam);
ScriptFunction function;
if (functions.TryGetValue(name, out function))
{
// Parse the parameters
++startParam;
List<string> parameters = ParseParameters(message.Substring(startParam, endParam - startParam));
// Parameters sanity check
if (parameters != null && parameters.Count == function.Parameters.Count)
{
// Convert the parameters into the required types
object[] objParameters = ConvertParameters(function, parameters);
if (objParameters != null)
{
// Find the avatar that sent this chat
SimulationObject avatar;
if (scene.TryGetObject(ownerID, out avatar))
{
// Lock the API, setup the member values, and invoke the function
lock (api)
{
api.Start(scene, avatar, UUID.Random(), false, false);
object ret = function.Method.Invoke(api, objParameters);
if (ret != null)
{
scene.ObjectChat(this, UUID.Zero, UUID.Zero, ChatAudibleLevel.Fully, ChatType.OwnerSay,
ChatSourceType.Object, "Script Console", Vector3.Zero, 0, ret.ToString());
}
return;
}
}
}
}
PrintFunctionUsage(function);
}
}
}
}
}
}
}

View File

@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
// FIXME: Implement this class
class TaskInventoryManager : IExtension<ISceneProvider>, ITaskInventoryProvider
{
ISceneProvider scene;
public TaskInventoryManager()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
return true;
}
public void Stop()
{
}
public UUID CreateItem(UUID agentID, UUID containerObjectID, string name, string description, InventoryType invType,
AssetType type, UUID assetID, UUID parentID, PermissionMask ownerMask, PermissionMask nextOwnerMask,
UUID ownerID, UUID creatorID, UUID transactionID, uint callbackID, bool sendPacket)
{
return UUID.Zero;
}
public bool RemoveItem(UUID agentID, UUID containerObjectID, UUID itemID)
{
return false;
}
public bool TryGetAsset(UUID containerObjectID, UUID assetID, out Asset asset)
{
asset = null;
return false;
}
public void ForEachItem(UUID containerObjectID, Action<InventoryTaskItem> action)
{
}
public InventoryTaskItem FindItem(UUID containerObjectID, Predicate<InventoryTaskItem> match)
{
return null;
}
public List<InventoryTaskItem> FindAllItems(UUID containerObjectID, Predicate<InventoryTaskItem> match)
{
return new List<InventoryTaskItem>();
}
}
}

View File

@@ -0,0 +1,359 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class TransferManager : IExtension<ISceneProvider>
{
ISceneProvider scene;
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
public TransferManager()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler));
scene.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler));
scene.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler));
scene.UDP.RegisterPacketCallback(PacketType.TransferRequest, new PacketCallback(TransferRequestHandler));
return true;
}
public void Stop()
{
}
#region Xfer System
void AssetUploadRequestHandler(Packet packet, Agent agent)
{
AssetUploadRequestPacket request = (AssetUploadRequestPacket)packet;
UUID assetID = UUID.Combine(request.AssetBlock.TransactionID, agent.SecureSessionID);
// Check if the asset is small enough to fit in a single packet
if (request.AssetBlock.AssetData.Length != 0)
{
// Create a new asset from the completed upload
Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, request.AssetBlock.AssetData);
if (asset == null)
{
Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning);
return;
}
Logger.DebugLog(String.Format("Storing uploaded asset {0} ({1})", assetID, asset.AssetType));
asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal);
// Store the asset
scene.Server.Assets.StoreAsset(asset);
// Send a success response
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
complete.AssetBlock.Success = true;
complete.AssetBlock.Type = request.AssetBlock.Type;
complete.AssetBlock.UUID = assetID;
scene.UDP.SendPacket(agent.ID, complete, PacketCategory.Inventory);
}
else
{
// Create a new (empty) asset for the upload
Asset asset = CreateAsset((AssetType)request.AssetBlock.Type, assetID, null);
if (asset == null)
{
Logger.Log("Failed to create asset from uploaded data", Helpers.LogLevel.Warning);
return;
}
Logger.DebugLog(String.Format("Starting upload for {0} ({1})", assetID, asset.AssetType));
asset.Temporary = (request.AssetBlock.Tempfile | request.AssetBlock.StoreLocal);
RequestXferPacket xfer = new RequestXferPacket();
xfer.XferID.DeleteOnCompletion = request.AssetBlock.Tempfile;
xfer.XferID.FilePath = 0;
xfer.XferID.Filename = Utils.EmptyBytes;
xfer.XferID.ID = request.AssetBlock.TransactionID.GetULong();
xfer.XferID.UseBigPackets = false;
xfer.XferID.VFileID = asset.AssetID;
xfer.XferID.VFileType = request.AssetBlock.Type;
// Add this asset to the current upload list
lock (CurrentUploads)
CurrentUploads[xfer.XferID.ID] = asset;
scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Inventory);
}
}
void SendXferPacketHandler(Packet packet, Agent agent)
{
SendXferPacketPacket xfer = (SendXferPacketPacket)packet;
Asset asset;
if (CurrentUploads.TryGetValue(xfer.XferID.ID, out asset))
{
if (asset.AssetData == null)
{
if (xfer.XferID.Packet != 0)
{
Logger.Log(String.Format("Received Xfer packet {0} before the first packet!",
xfer.XferID.Packet), Helpers.LogLevel.Error);
return;
}
uint size = Utils.BytesToUInt(xfer.DataPacket.Data);
asset.AssetData = new byte[size];
Buffer.BlockCopy(xfer.DataPacket.Data, 4, asset.AssetData, 0, xfer.DataPacket.Data.Length - 4);
// Confirm the first upload packet
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
confirm.XferID.ID = xfer.XferID.ID;
confirm.XferID.Packet = xfer.XferID.Packet;
scene.UDP.SendPacket(agent.ID, confirm, PacketCategory.Asset);
}
else
{
Buffer.BlockCopy(xfer.DataPacket.Data, 0, asset.AssetData, (int)xfer.XferID.Packet * 1000,
xfer.DataPacket.Data.Length);
// Confirm this upload packet
ConfirmXferPacketPacket confirm = new ConfirmXferPacketPacket();
confirm.XferID.ID = xfer.XferID.ID;
confirm.XferID.Packet = xfer.XferID.Packet;
scene.UDP.SendPacket(agent.ID, confirm, PacketCategory.Asset);
if ((xfer.XferID.Packet & (uint)0x80000000) != 0)
{
// Asset upload finished
Logger.DebugLog(String.Format("Completed Xfer upload of asset {0} ({1}", asset.AssetID, asset.AssetType));
lock (CurrentUploads)
CurrentUploads.Remove(xfer.XferID.ID);
scene.Server.Assets.StoreAsset(asset);
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
complete.AssetBlock.Success = true;
complete.AssetBlock.Type = (sbyte)asset.AssetType;
complete.AssetBlock.UUID = asset.AssetID;
scene.UDP.SendPacket(agent.ID, complete, PacketCategory.Asset);
}
}
}
else
{
Logger.DebugLog("Received a SendXferPacket for an unknown upload");
}
}
void AbortXferHandler(Packet packet, Agent agent)
{
AbortXferPacket abort = (AbortXferPacket)packet;
lock (CurrentUploads)
{
if (CurrentUploads.ContainsKey(abort.XferID.ID))
{
Logger.DebugLog(String.Format("Aborting Xfer {0}, result: {1}", abort.XferID.ID,
(TransferError)abort.XferID.Result));
CurrentUploads.Remove(abort.XferID.ID);
}
else
{
Logger.DebugLog(String.Format("Received an AbortXfer for an unknown xfer {0}",
abort.XferID.ID));
}
}
}
#endregion Xfer System
#region Transfer System
void TransferRequestHandler(Packet packet, Agent agent)
{
TransferRequestPacket request = (TransferRequestPacket)packet;
ChannelType channel = (ChannelType)request.TransferInfo.ChannelType;
SourceType source = (SourceType)request.TransferInfo.SourceType;
if (channel == ChannelType.Asset)
{
// Construct the response packet
TransferInfoPacket response = new TransferInfoPacket();
response.TransferInfo = new TransferInfoPacket.TransferInfoBlock();
response.TransferInfo.TransferID = request.TransferInfo.TransferID;
if (source == SourceType.Asset)
{
// Parse the request
UUID assetID = new UUID(request.TransferInfo.Params, 0);
AssetType type = (AssetType)(sbyte)Utils.BytesToInt(request.TransferInfo.Params, 16);
// Set the response channel type
response.TransferInfo.ChannelType = (int)ChannelType.Asset;
// Params
response.TransferInfo.Params = new byte[20];
Buffer.BlockCopy(assetID.GetBytes(), 0, response.TransferInfo.Params, 0, 16);
Buffer.BlockCopy(Utils.IntToBytes((int)type), 0, response.TransferInfo.Params, 16, 4);
// Check if we have this asset
Asset asset;
if (scene.Server.Assets.TryGetAsset(assetID, out asset))
{
if (asset.AssetType == type)
{
Logger.DebugLog(String.Format("Transferring asset {0} ({1})", asset.AssetID, asset.AssetType));
// Asset found
response.TransferInfo.Size = asset.AssetData.Length;
response.TransferInfo.Status = (int)StatusCode.OK;
response.TransferInfo.TargetType = (int)TargetType.Unknown; // Doesn't seem to be used by the client
scene.UDP.SendPacket(agent.ID, response, PacketCategory.Asset);
// Transfer system does not wait for ACKs, just sends all of the
// packets for this transfer out
const int MAX_CHUNK_SIZE = Settings.MAX_PACKET_SIZE - 100;
int processedLength = 0;
int packetNum = 0;
while (processedLength < asset.AssetData.Length)
{
TransferPacketPacket transfer = new TransferPacketPacket();
transfer.TransferData.ChannelType = (int)ChannelType.Asset;
transfer.TransferData.TransferID = request.TransferInfo.TransferID;
transfer.TransferData.Packet = packetNum++;
int chunkSize = Math.Min(asset.AssetData.Length - processedLength, MAX_CHUNK_SIZE);
transfer.TransferData.Data = new byte[chunkSize];
Buffer.BlockCopy(asset.AssetData, processedLength, transfer.TransferData.Data, 0, chunkSize);
processedLength += chunkSize;
if (processedLength >= asset.AssetData.Length)
transfer.TransferData.Status = (int)StatusCode.Done;
else
transfer.TransferData.Status = (int)StatusCode.OK;
scene.UDP.SendPacket(agent.ID, transfer, PacketCategory.Asset);
}
}
else
{
Logger.Log(String.Format(
"Request for asset {0} with type {1} does not match actual asset type {2}",
assetID, type, asset.AssetType), Helpers.LogLevel.Warning);
}
}
else
{
Logger.Log(String.Format("Request for missing asset {0} with type {1}",
assetID, type), Helpers.LogLevel.Warning);
// Asset not found
response.TransferInfo.Size = 0;
response.TransferInfo.Status = (int)StatusCode.UnknownSource;
response.TransferInfo.TargetType = (int)TargetType.Unknown;
scene.UDP.SendPacket(agent.ID, response, PacketCategory.Asset);
}
}
else if (source == SourceType.SimEstate)
{
UUID agentID = new UUID(request.TransferInfo.Params, 0);
UUID sessionID = new UUID(request.TransferInfo.Params, 16);
EstateAssetType type = (EstateAssetType)Utils.BytesToInt(request.TransferInfo.Params, 32);
Logger.Log("Please implement estate asset transfers", Helpers.LogLevel.Warning);
}
else if (source == SourceType.SimInventoryItem)
{
UUID agentID = new UUID(request.TransferInfo.Params, 0);
UUID sessionID = new UUID(request.TransferInfo.Params, 16);
UUID ownerID = new UUID(request.TransferInfo.Params, 32);
UUID taskID = new UUID(request.TransferInfo.Params, 48);
UUID itemID = new UUID(request.TransferInfo.Params, 64);
UUID assetID = new UUID(request.TransferInfo.Params, 80);
AssetType type = (AssetType)(sbyte)Utils.BytesToInt(request.TransferInfo.Params, 96);
if (taskID != UUID.Zero)
{
// Task (prim) inventory request
Logger.Log("Please implement task inventory transfers", Helpers.LogLevel.Warning);
}
else
{
// Agent inventory request
Logger.Log("Please implement agent inventory transfer", Helpers.LogLevel.Warning);
}
}
else
{
Logger.Log(String.Format(
"Received a TransferRequest that we don't know how to handle. Channel: {0}, Source: {1}",
channel, source), Helpers.LogLevel.Warning);
}
}
else
{
Logger.Log(String.Format(
"Received a TransferRequest that we don't know how to handle. Channel: {0}, Source: {1}",
channel, source), Helpers.LogLevel.Warning);
}
}
#endregion Transfer System
Asset CreateAsset(AssetType type, UUID assetID, byte[] data)
{
switch (type)
{
case AssetType.Bodypart:
return new AssetBodypart(assetID, data);
case AssetType.Clothing:
return new AssetClothing(assetID, data);
case AssetType.LSLBytecode:
return new AssetScriptBinary(assetID, data);
case AssetType.LSLText:
return new AssetScriptText(assetID, data);
case AssetType.Notecard:
return new AssetNotecard(assetID, data);
case AssetType.Texture:
return new AssetTexture(assetID, data);
case AssetType.Animation:
return new AssetAnimation(assetID, data);
case AssetType.CallingCard:
case AssetType.Folder:
case AssetType.Gesture:
case AssetType.ImageJPEG:
case AssetType.ImageTGA:
case AssetType.Landmark:
case AssetType.LostAndFoundFolder:
case AssetType.Object:
case AssetType.RootFolder:
case AssetType.Simstate:
case AssetType.SnapshotFolder:
case AssetType.Sound:
return new AssetSound(assetID, data);
case AssetType.SoundWAV:
case AssetType.TextureTGA:
case AssetType.TrashFolder:
case AssetType.Unknown:
default:
Logger.Log("Asset type " + type.ToString() + " not implemented!", Helpers.LogLevel.Warning);
return null;
}
}
}
}

View File

@@ -0,0 +1,637 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Threading;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public struct IncomingPacket
{
public UDPClient Client;
public Packet Packet;
}
public class OutgoingPacket
{
public Packet Packet;
/// <summary>Number of times this packet has been resent</summary>
public int ResendCount;
/// <summary>Environment.TickCount when this packet was last sent over the wire</summary>
public int TickCount;
/// <summary>Category this packet belongs to</summary>
public PacketCategory Category;
public OutgoingPacket(Packet packet, PacketCategory category)
{
Packet = packet;
Category = category;
}
}
public class UDPClient
{
/// <summary></summary>
public Agent Agent;
/// <summary></summary>
public IPEndPoint Address;
/// <summary>Sequence numbers of packets we've received (for duplicate checking)</summary>
public Queue<uint> PacketArchive = new Queue<uint>();
/// <summary>Packets we have sent that need to be ACKed by the client</summary>
public Dictionary<uint, OutgoingPacket> NeedAcks = new Dictionary<uint, OutgoingPacket>();
/// <summary>ACKs that are queued up, waiting to be sent to the client</summary>
public SortedList<uint, uint> PendingAcks = new SortedList<uint, uint>();
/// <summary>Current packet sequence number</summary>
public int CurrentSequence = 0;
Timer ackTimer;
UDPServer udpServer;
public UDPClient(UDPServer server, Agent agent, IPEndPoint address)
{
udpServer = server;
Agent = agent;
Address = address;
ackTimer = new Timer(new TimerCallback(AckTimer_Elapsed), null, Settings.NETWORK_TICK_INTERVAL,
Settings.NETWORK_TICK_INTERVAL);
}
public void Shutdown()
{
ackTimer.Dispose();
}
private void AckTimer_Elapsed(object obj)
{
udpServer.SendAcks(this);
udpServer.ResendUnacked(this);
}
}
public class UDPManager : IExtension<ISceneProvider>, IUDPProvider
{
ISceneProvider scene;
UDPServer udpServer;
public event OutgoingPacketCallback OnOutgoingPacket;
public UDPManager()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
udpServer = new UDPServer(scene.IPAndPort, scene);
return true;
}
public void Stop()
{
if (udpServer != null)
udpServer.Stop();
}
public void AddClient(Agent agent, IPEndPoint endpoint)
{
udpServer.AddClient(agent, endpoint);
}
public bool RemoveClient(Agent agent)
{
return udpServer.RemoveClient(agent);
}
public uint CreateCircuit(Agent agent)
{
return udpServer.CreateCircuit(agent);
}
public void SendPacket(UUID agentID, Packet packet, PacketCategory category)
{
if (OnOutgoingPacket == null || OnOutgoingPacket(packet, agentID, category))
udpServer.SendPacket(agentID, packet, category);
}
public void BroadcastPacket(Packet packet, PacketCategory category)
{
if (OnOutgoingPacket == null || OnOutgoingPacket(packet, UUID.Zero, category))
udpServer.BroadcastPacket(packet, category);
}
public void RegisterPacketCallback(PacketType type, PacketCallback callback)
{
udpServer.RegisterPacketCallback(type, callback);
}
}
public class UDPServer : UDPBase
{
/// <summary>This is only used to fetch unassociated agents, which will
/// be exposed through a login interface at some point</summary>
ISceneProvider scene;
/// <summary>Handlers for incoming packets</summary>
PacketEventDictionary packetEvents = new PacketEventDictionary();
/// <summary>Incoming packets that are awaiting handling</summary>
BlockingQueue<IncomingPacket> packetInbox = new BlockingQueue<IncomingPacket>(Settings.PACKET_INBOX_SIZE);
/// <summary></summary>
DoubleDictionary<UUID, IPEndPoint, UDPClient> clients = new DoubleDictionary<UUID, IPEndPoint, UDPClient>();
/// <summary></summary>
Dictionary<uint, Agent> unassociatedAgents = new Dictionary<uint, Agent>();
/// <summary></summary>
int currentCircuitCode = 0;
// FIXME: Upgrade UDPBase to be able to listen on different endpoints
public UDPServer(IPEndPoint endpoint, ISceneProvider scene)
: base(endpoint.Port)
{
this.scene = scene;
Start();
// Start the incoming packet processing thread
Thread incomingThread = new Thread(new ThreadStart(IncomingPacketHandler));
incomingThread.Start();
}
public void RegisterPacketCallback(PacketType type, PacketCallback callback)
{
packetEvents.RegisterEvent(type, callback);
}
public void AddClient(Agent agent, IPEndPoint endpoint)
{
UDPClient client = new UDPClient(this, agent, endpoint);
clients.Add(agent.ID, endpoint, client);
}
public bool RemoveClient(Agent agent)
{
UDPClient client;
if (clients.TryGetValue(agent.ID, out client))
{
client.Shutdown();
return clients.Remove(agent.ID, client.Address);
}
else
return false;
}
public uint CreateCircuit(Agent agent)
{
uint circuitCode = (uint)Interlocked.Increment(ref currentCircuitCode);
// Put this client in the list of clients that have not been associated with an IPEndPoint yet
lock (unassociatedAgents)
unassociatedAgents[circuitCode] = agent;
Logger.Log("Created circuit " + circuitCode + " for " + agent.FullName, Helpers.LogLevel.Info);
return circuitCode;
}
public void BroadcastPacket(Packet packet, PacketCategory category)
{
clients.ForEach(
delegate(UDPClient client) { SendPacket(client, new OutgoingPacket(packet, category)); });
}
public void SendPacket(UUID agentID, Packet packet, PacketCategory category)
{
// Look up the UDPClient this is going to
UDPClient client;
if (clients.TryGetValue(agentID, out client))
SendPacket(client, new OutgoingPacket(packet, category));
}
void SendPacket(UDPClient client, OutgoingPacket outgoingPacket)
{
Packet packet = outgoingPacket.Packet;
byte[] buffer;
int bytes;
// Update the sent time for this packet
outgoingPacket.TickCount = Environment.TickCount;
if (!packet.Header.Resent)
{
// Reset to zero if we've hit the upper sequence number limit
Interlocked.CompareExchange(ref client.CurrentSequence, 0, 0xFFFFFF);
// Increment and fetch the current sequence number
uint sequence = (uint)Interlocked.Increment(ref client.CurrentSequence);
packet.Header.Sequence = sequence;
if (packet.Header.Reliable)
{
// Add this packet to the list of ACK responses we are waiting on from this client
lock (client.NeedAcks)
client.NeedAcks[sequence] = outgoingPacket;
// This packet is reliable and not a resend, check if the conditions are favorable
// to ACK appending
if (packet.Type != PacketType.PacketAck)
{
lock (client.PendingAcks)
{
int count = client.PendingAcks.Count;
if (count > 0 && count < 10)
{
// Append all of the queued up outgoing ACKs to this packet
packet.Header.AckList = new uint[count];
for (int i = 0; i < count; i++)
packet.Header.AckList[i] = client.PendingAcks.Values[i];
client.PendingAcks.Clear();
packet.Header.AppendedAcks = true;
}
}
}
}
}
else
{
// This packet has already been sent out once, strip any appended ACKs
// off it and reinsert them into the outgoing ACK queue under the
// assumption that this packet will continually be rejected from the
// client or that the appended ACKs are possibly making the delivery fail
if (packet.Header.AckList.Length > 0)
{
Logger.DebugLog(String.Format("Purging ACKs from packet #{0} ({1}) which will be resent.",
packet.Header.Sequence, packet.GetType()));
lock (client.PendingAcks)
{
foreach (uint ack in packet.Header.AckList)
{
if (!client.PendingAcks.ContainsKey(ack))
client.PendingAcks[ack] = ack;
}
}
packet.Header.AppendedAcks = false;
packet.Header.AckList = new uint[0];
}
}
// Serialize the packet
buffer = packet.ToBytes();
bytes = buffer.Length;
//Stats.SentBytes += (ulong)bytes;
//++Stats.SentPackets;
UDPPacketBuffer buf = new UDPPacketBuffer(client.Address);
// Zerocode if needed
if (packet.Header.Zerocoded)
bytes = Helpers.ZeroEncode(buffer, bytes, buf.Data);
else
Buffer.BlockCopy(buffer, 0, buf.Data, 0, bytes);
buf.DataLength = bytes;
AsyncBeginSend(buf);
}
void QueueAck(UDPClient client, uint ack)
{
// Add this packet to the list of ACKs that need to be sent out
lock (client.PendingAcks)
client.PendingAcks[ack] = ack;
// Send out ACKs if we have a lot of them
if (client.PendingAcks.Count >= 10)
SendAcks(client);
}
void ProcessAcks(UDPClient client, List<uint> acks)
{
lock (client.NeedAcks)
{
foreach (uint ack in acks)
client.NeedAcks.Remove(ack);
}
}
void SendAck(UDPClient client, uint ack)
{
PacketAckPacket acks = new PacketAckPacket();
acks.Header.Reliable = false;
acks.Packets = new PacketAckPacket.PacketsBlock[1];
acks.Packets[0] = new PacketAckPacket.PacketsBlock();
acks.Packets[0].ID = ack;
SendPacket(client, new OutgoingPacket(acks, PacketCategory.Overhead));
}
public void SendAcks(UDPClient client)
{
PacketAckPacket acks = null;
lock (client.PendingAcks)
{
int count = client.PendingAcks.Count;
if (count > 250)
{
Logger.Log("Too many ACKs queued up!", Helpers.LogLevel.Error);
return;
}
else if (count > 0)
{
acks = new PacketAckPacket();
acks.Header.Reliable = false;
acks.Packets = new PacketAckPacket.PacketsBlock[count];
for (int i = 0; i < count; i++)
{
acks.Packets[i] = new PacketAckPacket.PacketsBlock();
acks.Packets[i].ID = client.PendingAcks.Values[i];
}
client.PendingAcks.Clear();
}
}
if (acks != null)
SendPacket(client, new OutgoingPacket(acks, PacketCategory.Overhead));
}
public void ResendUnacked(UDPClient client)
{
if (client.NeedAcks.Count > 0)
{
OutgoingPacket[] array;
int now = Environment.TickCount;
lock (client.NeedAcks)
{
// Create a temporary copy of the outgoing packets array to iterate over
array = new OutgoingPacket[client.NeedAcks.Count];
client.NeedAcks.Values.CopyTo(array, 0);
}
// Resend packets
for (int i = 0; i < array.Length; i++)
{
OutgoingPacket outgoing = array[i];
// FIXME: Make 4000 and 3 .ini settings
if (outgoing.TickCount != 0 && now - outgoing.TickCount > 4000)
{
if (outgoing.ResendCount < 3)
{
//Logger.DebugLog(String.Format("Resending packet #{0} ({1}), {2}ms have passed",
// outgoing.Packet.Header.Sequence, outgoing.Packet.GetType(), now - outgoing.TickCount));
// The TickCount will be set to the current time when the packet
// is actually sent out again
outgoing.TickCount = 0;
outgoing.Packet.Header.Resent = true;
++outgoing.ResendCount;
//++Stats.ResentPackets;
try
{
SendPacket(client, outgoing);
}
catch (NullReferenceException)
{
// Programming error elsewhere can put bad packets in the outgoing queue.
// These will never successfully serialize, so just drop them
Logger.Log(String.Format("Dropping bad packet #{0} ({1}) from the outgoing queue",
outgoing.Packet.Header.Sequence, outgoing.Packet.GetType()),
Helpers.LogLevel.Warning);
lock (client.NeedAcks) client.NeedAcks.Remove(outgoing.Packet.Header.Sequence);
}
}
else
{
Logger.Log(String.Format("Dropping packet #{0} ({1}) after {2} failed attempts",
outgoing.Packet.Header.Sequence, outgoing.Packet.GetType(), outgoing.ResendCount),
Helpers.LogLevel.Warning);
lock (client.NeedAcks) client.NeedAcks.Remove(outgoing.Packet.Header.Sequence);
//Disconnect an agent if no packets are received for some time
//FIXME: Make 60000 an .ini setting
if (Environment.TickCount - client.Agent.TickLastPacketReceived > 60000)
{
Logger.Log(String.Format("Ack timeout for {0}, disconnecting", client.Agent.FullName),
Helpers.LogLevel.Warning);
scene.ObjectRemove(this, client.Agent.ID);
return;
}
}
}
}
}
}
protected override void PacketReceived(UDPPacketBuffer buffer)
{
UDPClient client = null;
Packet packet = null;
int packetEnd = buffer.DataLength - 1;
IPEndPoint address = (IPEndPoint)buffer.RemoteEndPoint;
// Decoding
try
{
packet = Packet.BuildPacket(buffer.Data, ref packetEnd, buffer.ZeroData);
}
catch (MalformedDataException)
{
Logger.Log(String.Format("Malformed data, cannot parse packet:\n{0}",
Utils.BytesToHexString(buffer.Data, buffer.DataLength, null)), Helpers.LogLevel.Error);
}
// Fail-safe check
if (packet == null)
{
Logger.Log("Couldn't build a message from the incoming data", Helpers.LogLevel.Warning);
return;
}
//Stats.RecvBytes += (ulong)buffer.DataLength;
//++Stats.RecvPackets;
if (packet.Type == PacketType.UseCircuitCode)
{
UseCircuitCodePacket useCircuitCode = (UseCircuitCodePacket)packet;
Agent agent;
if (CompleteAgentConnection(useCircuitCode.CircuitCode.Code, out agent))
{
// Sanity check that the agent isn't already logged in
if (clients.ContainsKey(agent.ID))
{
Logger.Log("Client UDP reference already exists for " + agent.ID.ToString() + ", removing",
Helpers.LogLevel.Warning);
scene.ObjectRemove(this, agent.ID);
clients.Remove(agent.ID);
}
Logger.DebugLog(String.Format("Adding client {0} from {1}", agent.FullName, address));
AddClient(agent, address);
if (clients.TryGetValue(agent.ID, out client))
{
Logger.Log("Activated UDP circuit " + useCircuitCode.CircuitCode.Code, Helpers.LogLevel.Info);
}
else
{
Logger.Log("Failed to locate newly created UDPClient", Helpers.LogLevel.Error);
return;
}
}
else
{
Logger.Log("Received a UseCircuitCode packet for an unrecognized circuit: " +
useCircuitCode.CircuitCode.Code.ToString(), Helpers.LogLevel.Warning);
return;
}
}
else
{
// Determine which agent this packet came from
if (!clients.TryGetValue(address, out client))
{
Logger.Log("Received UDP packet from an unrecognized source: " + address.ToString(),
Helpers.LogLevel.Warning);
return;
}
}
client.Agent.TickLastPacketReceived = Environment.TickCount;
// Reliable handling
if (packet.Header.Reliable)
{
// Queue up this sequence number for acknowledgement
QueueAck(client, (uint)packet.Header.Sequence);
//if (packet.Header.Resent) ++Stats.ReceivedResends;
}
// Inbox insertion
IncomingPacket incomingPacket;
incomingPacket.Client = client;
incomingPacket.Packet = packet;
// TODO: Prioritize the queue
packetInbox.Enqueue(incomingPacket);
}
protected override void PacketSent(UDPPacketBuffer buffer, int bytesSent)
{
}
void IncomingPacketHandler()
{
IncomingPacket incomingPacket = new IncomingPacket();
Packet packet = null;
UDPClient client = null;
while (IsRunning)
{
// Reset packet to null for the check below
packet = null;
if (packetInbox.Dequeue(100, ref incomingPacket))
{
packet = incomingPacket.Packet;
client = incomingPacket.Client;
if (packet != null)
{
#region ACK accounting
// Check the archives to see whether we already received this packet
lock (client.PacketArchive)
{
if (client.PacketArchive.Contains(packet.Header.Sequence))
{
if (packet.Header.Resent)
{
Logger.DebugLog("Received resent packet #" + packet.Header.Sequence);
}
else
{
Logger.Log(String.Format("Received a duplicate of packet #{0}, current type: {1}",
packet.Header.Sequence, packet.Type), Helpers.LogLevel.Warning);
}
// Avoid firing a callback twice for the same packet
continue;
}
else
{
// Keep the PacketArchive size within a certain capacity
while (client.PacketArchive.Count >= Settings.PACKET_ARCHIVE_SIZE)
{
client.PacketArchive.Dequeue(); client.PacketArchive.Dequeue();
client.PacketArchive.Dequeue(); client.PacketArchive.Dequeue();
}
client.PacketArchive.Enqueue(packet.Header.Sequence);
}
}
#endregion ACK accounting
#region ACK handling
// Handle appended ACKs
if (packet.Header.AppendedAcks)
{
lock (client.NeedAcks)
{
for (int i = 0; i < packet.Header.AckList.Length; i++)
client.NeedAcks.Remove(packet.Header.AckList[i]);
}
}
// Handle PacketAck packets
if (packet.Type == PacketType.PacketAck)
{
PacketAckPacket ackPacket = (PacketAckPacket)packet;
lock (client.NeedAcks)
{
for (int i = 0; i < ackPacket.Packets.Length; i++)
client.NeedAcks.Remove(ackPacket.Packets[i].ID);
}
}
#endregion ACK handling
packetEvents.BeginRaiseEvent(packet.Type, packet, client.Agent);
}
}
}
}
bool CompleteAgentConnection(uint circuitCode, out Agent agent)
{
lock (unassociatedAgents)
{
if (unassociatedAgents.TryGetValue(circuitCode, out agent))
{
unassociatedAgents.Remove(circuitCode);
scene.AgentAdd(this, agent, PrimFlags.None);
return true;
}
else
{
return false;
}
}
}
}
}

View File

@@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
namespace Simian
{
// FIXME: Implement this class
class XScriptEngine : IExtension<ISceneProvider>, IScriptEngine
{
ISceneProvider scene;
public XScriptEngine()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
return true;
}
public void Stop()
{
}
public bool PostScriptEvent(UUID scriptID, EventParams parms)
{
return false;
}
public bool PostObjectEvent(UUID hostObjectID, EventParams parms)
{
return false;
}
public void SetTimerEvent(UUID scriptID, double seconds)
{
}
public DetectParams GetDetectParams(UUID scriptID, int detectIndex)
{
DetectParams parms = new DetectParams();
return parms;
}
public bool GetScriptState(UUID scriptID)
{
return false;
}
public void SetScriptState(UUID scriptID, bool state)
{
}
public void SetStartParameter(UUID scriptID, int startParam)
{
}
public int GetStartParameter(UUID scriptID)
{
return 0;
}
public void SetScriptMinEventDelay(UUID scriptID, double minDelay)
{
}
public void TriggerState(UUID scriptID, string newState)
{
}
public void ApiResetScript(UUID scriptID)
{
}
public void ResetScript(UUID scriptID)
{
}
public int AddListener(UUID scriptID, UUID hostObjectID, int channel, string name, UUID keyID, string message)
{
return 0;
}
public void RemoveListener(UUID scriptID, int handle)
{
}
public void RemoveListeners(UUID scriptID)
{
}
public void SetListenerState(UUID scriptID, int handle, bool enabled)
{
}
public void SensorOnce(UUID scriptID, UUID hostObjectID, string name, UUID keyID, int type, double range, double arc)
{
}
public void SensorRepeat(UUID scriptID, UUID hostObjectID, string name, UUID keyID, int type, double range, double arc, double rate)
{
}
public void SensorRemove(UUID scriptID)
{
}
}
}