Files
libremetaverse/Programs/Simian/LLUDP/LLAssets.cs
John Hurliman c20afbbf80 * Added InventoryItemFlags, which is actually only the upper half of the Flags field for inventory items. Stores slam bits, permission override flags, and other things we don't use at all right now
[Simian]
* Initial task inventory support. Move, remove, and RezScript are not supported yet
* SimulationObject Frozen and RotationAxis properties now point to the root prim in the linkset

git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2503 52acb1d6-8a22-11de-b505-999d5b087335
2009-03-19 00:25:03 +00:00

445 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using ExtensionLoader;
using OpenMetaverse;
using OpenMetaverse.Packets;
namespace Simian
{
public class LLAssets : IExtension<ISceneProvider>
{
ISceneProvider scene;
Dictionary<ulong, XferDownload> currentDownloads = new Dictionary<ulong, XferDownload>();
Dictionary<ulong, Asset> currentUploads = new Dictionary<ulong, Asset>();
public LLAssets()
{
}
public bool Start(ISceneProvider scene)
{
this.scene = scene;
scene.UDP.RegisterPacketCallback(PacketType.RequestXfer, RequestXferHandler);
scene.UDP.RegisterPacketCallback(PacketType.ConfirmXferPacket, ConfirmXferPacketHandler);
scene.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, AssetUploadRequestHandler);
scene.UDP.RegisterPacketCallback(PacketType.SendXferPacket, SendXferPacketHandler);
scene.UDP.RegisterPacketCallback(PacketType.AbortXfer, AbortXferHandler);
scene.UDP.RegisterPacketCallback(PacketType.TransferRequest, TransferRequestHandler);
return true;
}
public void Stop()
{
}
#region Xfer System
void RequestXferHandler(Packet packet, Agent agent)
{
RequestXferPacket request = (RequestXferPacket)packet;
string filename = Utils.BytesToString(request.XferID.Filename);
byte[] assetData;
if (scene.TaskInventory.TryGetTaskFile(filename, out assetData))
{
SendXferPacketPacket xfer = new SendXferPacketPacket();
xfer.XferID.ID = request.XferID.ID;
if (assetData.Length < 1000)
{
xfer.XferID.Packet = 0x80000000;
xfer.DataPacket.Data = new byte[assetData.Length + 4];
Utils.IntToBytes(assetData.Length, xfer.DataPacket.Data, 0);
Buffer.BlockCopy(assetData, 0, xfer.DataPacket.Data, 4, assetData.Length);
scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Asset);
Logger.DebugLog("Completed single packet xfer download of " + filename);
}
else
{
xfer.XferID.Packet = 0;
xfer.DataPacket.Data = new byte[1000 + 4];
Utils.IntToBytes(assetData.Length, xfer.DataPacket.Data, 0);
Buffer.BlockCopy(assetData, 0, xfer.DataPacket.Data, 4, 1000);
// We don't need the entire XferDownload class, just the asset data and the current packet number
XferDownload download = new XferDownload();
download.AssetData = assetData;
download.PacketNum = 1;
download.Filename = filename;
lock (currentDownloads)
currentDownloads[request.XferID.ID] = download;
scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Asset);
}
}
else
{
Logger.Log("Got a RequestXfer for an unknown file: " + filename, Helpers.LogLevel.Warning);
}
// lock (currentDownloads)
}
void ConfirmXferPacketHandler(Packet packet, Agent agent)
{
ConfirmXferPacketPacket confirm = (ConfirmXferPacketPacket)packet;
XferDownload download;
if (currentDownloads.TryGetValue(confirm.XferID.ID, out download))
{
// Send the next packet
SendXferPacketPacket xfer = new SendXferPacketPacket();
xfer.XferID.ID = confirm.XferID.ID;
int bytesRemaining = (int)(download.AssetData.Length - (download.PacketNum * 1000));
if (bytesRemaining > 1000)
{
xfer.DataPacket.Data = new byte[1000];
Buffer.BlockCopy(download.AssetData, (int)download.PacketNum * 1000, xfer.DataPacket.Data, 0, 1000);
xfer.XferID.Packet = download.PacketNum++;
}
else
{
// Last packet
xfer.DataPacket.Data = new byte[bytesRemaining];
Buffer.BlockCopy(download.AssetData, (int)download.PacketNum * 1000, xfer.DataPacket.Data, 0, bytesRemaining);
xfer.XferID.Packet = download.PacketNum++ | 0x80000000;
lock (currentDownloads)
currentDownloads.Remove(confirm.XferID.ID);
Logger.DebugLog("Completing xfer download for: " + download.Filename);
}
scene.UDP.SendPacket(agent.ID, xfer, PacketCategory.Asset);
}
}
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;
}
}
}
}