* Split up asset store and transfer manager in Simian into two extensions
* Added PeriscopeTransferManager to handle animation and sound transfers * Misc. transfer bugfixes in libomv * Misc. improvement to TexturePipeline and some numbers tweaking * Attempted to pass object touch events through to foreign grid in Periscope, may not be working * Added new chat commands in Periscope git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2385 52acb1d6-8a22-11de-b505-999d5b087335
This commit is contained in:
@@ -41,7 +41,7 @@ namespace SimExport
|
||||
running = true;
|
||||
|
||||
client = new GridClient();
|
||||
texturePipeline = new TexturePipeline(client);
|
||||
texturePipeline = new TexturePipeline(client, 10);
|
||||
texturePipeline.OnDownloadFinished += new TexturePipeline.DownloadFinishedCallback(texturePipeline_OnDownloadFinished);
|
||||
|
||||
//Settings.LOG_LEVEL = Helpers.LogLevel.Info;
|
||||
@@ -345,7 +345,7 @@ namespace SimExport
|
||||
for (int i = 0; i < te.FaceTextures.Length; i++)
|
||||
{
|
||||
if (te.FaceTextures[i] != null && !texturesFinished.ContainsKey(te.FaceTextures[i].TextureID))
|
||||
texturePipeline.RequestTexture(te.FaceTextures[i].TextureID);
|
||||
texturePipeline.RequestTexture(te.FaceTextures[i].TextureID, ImageType.Normal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,346 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using OpenMetaverse;
|
||||
|
||||
/*
|
||||
* the onnewprim function will add missing texture uuids to the download queue,
|
||||
* and a separate thread will pull entries off that queue.
|
||||
* if they exist in the cache it will add that texture to a dictionary that the rendering loop accesses,
|
||||
* otherwise it will start the download.
|
||||
* the ondownloaded function will put the new texture in the same dictionary
|
||||
*
|
||||
*
|
||||
* Easy Start:
|
||||
* subscribe to OnImageRenderReady event
|
||||
* send request with RequestTexture()
|
||||
*
|
||||
* when OnImageRenderReady fires:
|
||||
* request image data with GetTextureToRender() using key returned in OnImageRenderReady event
|
||||
* (optionally) use RemoveFromPipeline() with key to cleanup dictionary
|
||||
*/
|
||||
|
||||
namespace SimExport
|
||||
{
|
||||
class TaskInfo
|
||||
{
|
||||
public UUID RequestID;
|
||||
public int RequestNbr;
|
||||
|
||||
|
||||
public TaskInfo(UUID reqID, int reqNbr)
|
||||
{
|
||||
RequestID = reqID;
|
||||
RequestNbr = reqNbr;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Texture request download handler, allows a configurable number of download slots
|
||||
/// </summary>
|
||||
public class TexturePipeline
|
||||
{
|
||||
private static GridClient Client;
|
||||
|
||||
// queue for requested images
|
||||
private Queue<UUID> RequestQueue;
|
||||
|
||||
// list of current requests in process
|
||||
private Dictionary<UUID, int> CurrentRequests;
|
||||
|
||||
private static AutoResetEvent[] resetEvents;
|
||||
|
||||
private static int[] threadpoolSlots;
|
||||
|
||||
/// <summary>
|
||||
/// For keeping track of active threads available/downloading textures
|
||||
/// </summary>
|
||||
public static int[] ThreadpoolSlots
|
||||
{
|
||||
get { lock (threadpoolSlots) { return threadpoolSlots; } }
|
||||
set { lock (threadpoolSlots) { threadpoolSlots = value; } }
|
||||
}
|
||||
|
||||
public int QueuedCount { get { return RequestQueue.Count; } }
|
||||
public int CurrentCount { get { return CurrentRequests.Count; } }
|
||||
|
||||
// storage for images ready to render
|
||||
private Dictionary<UUID, ImageDownload> RenderReady;
|
||||
|
||||
// maximum allowed concurrent requests at once
|
||||
const int MAX_TEXTURE_REQUESTS = 10;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="success"></param>
|
||||
public delegate void DownloadFinishedCallback(UUID id, bool success);
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="id"></param>
|
||||
/// <param name="received"></param>
|
||||
/// <param name="total"></param>
|
||||
public delegate void DownloadProgressCallback(UUID image, int recieved, int total);
|
||||
|
||||
/// <summary>Fired when a texture download completes</summary>
|
||||
public event DownloadFinishedCallback OnDownloadFinished;
|
||||
/// <summary></summary>
|
||||
public event DownloadProgressCallback OnDownloadProgress;
|
||||
|
||||
private Thread downloadMaster;
|
||||
private bool Running;
|
||||
|
||||
private AssetManager.ImageReceivedCallback DownloadCallback;
|
||||
private AssetManager.ImageReceiveProgressCallback DownloadProgCallback;
|
||||
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
/// <param name="client">Reference to <code>SecondLife</code> client</param>
|
||||
public TexturePipeline(GridClient client)
|
||||
{
|
||||
Running = true;
|
||||
|
||||
RequestQueue = new Queue<UUID>();
|
||||
CurrentRequests = new Dictionary<UUID, int>(MAX_TEXTURE_REQUESTS);
|
||||
|
||||
RenderReady = new Dictionary<UUID, ImageDownload>();
|
||||
|
||||
resetEvents = new AutoResetEvent[MAX_TEXTURE_REQUESTS];
|
||||
threadpoolSlots = new int[MAX_TEXTURE_REQUESTS];
|
||||
|
||||
// pre-configure autoreset events/download slots
|
||||
for (int i = 0; i < MAX_TEXTURE_REQUESTS; i++)
|
||||
{
|
||||
resetEvents[i] = new AutoResetEvent(false);
|
||||
threadpoolSlots[i] = -1;
|
||||
}
|
||||
|
||||
Client = client;
|
||||
|
||||
DownloadCallback = new AssetManager.ImageReceivedCallback(Assets_OnImageReceived);
|
||||
DownloadProgCallback = new AssetManager.ImageReceiveProgressCallback(Assets_OnImageReceiveProgress);
|
||||
Client.Assets.OnImageReceived += DownloadCallback;
|
||||
Client.Assets.OnImageReceiveProgress += DownloadProgCallback;
|
||||
|
||||
// Fire up the texture download thread
|
||||
downloadMaster = new Thread(new ThreadStart(DownloadThread));
|
||||
downloadMaster.Start();
|
||||
}
|
||||
|
||||
public void Shutdown()
|
||||
{
|
||||
Client.Assets.OnImageReceived -= DownloadCallback;
|
||||
Client.Assets.OnImageReceiveProgress -= DownloadProgCallback;
|
||||
|
||||
RequestQueue.Clear();
|
||||
|
||||
for (int i = 0; i < resetEvents.Length; i++)
|
||||
if (resetEvents[i] != null)
|
||||
resetEvents[i].Set();
|
||||
|
||||
Running = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Request a texture be downloaded, once downloaded OnImageRenderReady event will be fired
|
||||
/// containing texture key which can be used to retrieve texture with GetTextureToRender method
|
||||
/// </summary>
|
||||
/// <param name="textureID">id of Texture to request</param>
|
||||
public void RequestTexture(UUID textureID)
|
||||
{
|
||||
if (Client.Assets.Cache.HasImage(textureID))
|
||||
{
|
||||
// Add to rendering dictionary
|
||||
lock (RenderReady)
|
||||
{
|
||||
if (!RenderReady.ContainsKey(textureID))
|
||||
{
|
||||
RenderReady.Add(textureID, Client.Assets.Cache.GetCachedImage(textureID));
|
||||
|
||||
// Let any subscribers know about it
|
||||
if (OnDownloadFinished != null)
|
||||
{
|
||||
OnDownloadFinished(textureID, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// This image has already been served up, ignore this request
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lock (RequestQueue)
|
||||
{
|
||||
// Make sure we aren't already downloading the texture
|
||||
if (!RequestQueue.Contains(textureID) && !CurrentRequests.ContainsKey(textureID))
|
||||
{
|
||||
RequestQueue.Enqueue(textureID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// retrieve texture information from dictionary
|
||||
/// </summary>
|
||||
/// <param name="textureID">Texture ID</param>
|
||||
/// <returns>ImageDownload object</returns>
|
||||
public ImageDownload GetTextureToRender(UUID textureID)
|
||||
{
|
||||
ImageDownload renderable = new ImageDownload();
|
||||
lock (RenderReady)
|
||||
{
|
||||
if (RenderReady.ContainsKey(textureID))
|
||||
{
|
||||
renderable = RenderReady[textureID];
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Log("Requested texture data for texture that does not exist in dictionary", Helpers.LogLevel.Warning);
|
||||
}
|
||||
return renderable;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove no longer necessary texture from dictionary
|
||||
/// </summary>
|
||||
/// <param name="textureID"></param>
|
||||
public void RemoveFromPipeline(UUID textureID)
|
||||
{
|
||||
lock (RenderReady)
|
||||
{
|
||||
if (RenderReady.ContainsKey(textureID))
|
||||
RenderReady.Remove(textureID);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Master Download Thread, Queues up downloads in the threadpool
|
||||
/// </summary>
|
||||
private void DownloadThread()
|
||||
{
|
||||
int reqNbr;
|
||||
|
||||
while (Running)
|
||||
{
|
||||
if (RequestQueue.Count > 0)
|
||||
{
|
||||
reqNbr = -1;
|
||||
// find available slot for reset event
|
||||
for (int i = 0; i < threadpoolSlots.Length; i++)
|
||||
{
|
||||
if (threadpoolSlots[i] == -1)
|
||||
{
|
||||
threadpoolSlots[i] = 1;
|
||||
reqNbr = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (reqNbr != -1)
|
||||
{
|
||||
UUID requestID;
|
||||
lock (RequestQueue)
|
||||
requestID = RequestQueue.Dequeue();
|
||||
|
||||
Logger.DebugLog(String.Format("Sending Worker thread new download request {0}", reqNbr));
|
||||
ThreadPool.QueueUserWorkItem(new WaitCallback(textureRequestDoWork), new TaskInfo(requestID, reqNbr));
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Queue was empty, let's give up some CPU time
|
||||
Thread.Sleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
void textureRequestDoWork(Object threadContext)
|
||||
{
|
||||
TaskInfo ti = (TaskInfo)threadContext;
|
||||
|
||||
lock (CurrentRequests)
|
||||
{
|
||||
if (CurrentRequests.ContainsKey(ti.RequestID))
|
||||
{
|
||||
threadpoolSlots[ti.RequestNbr] = -1;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentRequests.Add(ti.RequestID, ti.RequestNbr);
|
||||
}
|
||||
}
|
||||
|
||||
Logger.DebugLog(String.Format("Worker {0} Requesting {1}", ti.RequestNbr, ti.RequestID));
|
||||
|
||||
resetEvents[ti.RequestNbr].Reset();
|
||||
Client.Assets.RequestImage(ti.RequestID, ImageType.Normal);
|
||||
|
||||
// don't release this worker slot until texture is downloaded or timeout occurs
|
||||
if (!resetEvents[ti.RequestNbr].WaitOne(30 * 1000, false))
|
||||
{
|
||||
// Timed out
|
||||
Logger.Log("Worker " + ti.RequestNbr + " Timeout waiting for Texture " + ti.RequestID + " to Download", Helpers.LogLevel.Warning);
|
||||
|
||||
lock (CurrentRequests)
|
||||
CurrentRequests.Remove(ti.RequestID);
|
||||
}
|
||||
|
||||
// free up this download slot
|
||||
threadpoolSlots[ti.RequestNbr] = -1;
|
||||
}
|
||||
|
||||
private void Assets_OnImageReceived(ImageDownload image, AssetTexture asset)
|
||||
{
|
||||
// Free up this slot in the ThreadPool
|
||||
lock (CurrentRequests)
|
||||
{
|
||||
int requestNbr;
|
||||
if (asset != null && CurrentRequests.TryGetValue(image.ID, out requestNbr))
|
||||
{
|
||||
Logger.DebugLog(String.Format("Worker {0} Downloaded texture {1}", requestNbr, image.ID));
|
||||
resetEvents[requestNbr].Set();
|
||||
CurrentRequests.Remove(image.ID);
|
||||
}
|
||||
}
|
||||
|
||||
if (image.Success)
|
||||
{
|
||||
lock (RenderReady)
|
||||
{
|
||||
if (!RenderReady.ContainsKey(image.ID))
|
||||
{
|
||||
// Add to rendering dictionary
|
||||
RenderReady.Add(image.ID, image);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine(String.Format("Download of texture {0} failed. NotFound={1}", image.ID, image.NotFound));
|
||||
}
|
||||
|
||||
// Let any subscribers know about it
|
||||
if (OnDownloadFinished != null)
|
||||
{
|
||||
OnDownloadFinished(image.ID, image.Success);
|
||||
}
|
||||
}
|
||||
|
||||
private void Assets_OnImageReceiveProgress(UUID image, int lastPacket, int recieved, int total)
|
||||
{
|
||||
if (OnDownloadProgress != null)
|
||||
{
|
||||
OnDownloadProgress(image, recieved, total);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ using System.IO;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Imaging;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
@@ -14,7 +13,6 @@ namespace Simian.Extensions
|
||||
|
||||
Simian server;
|
||||
Dictionary<UUID, Asset> AssetStore = new Dictionary<UUID, Asset>();
|
||||
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
|
||||
string UploadDir;
|
||||
|
||||
public AssetManager()
|
||||
@@ -41,11 +39,6 @@ namespace Simian.Extensions
|
||||
|
||||
LoadAssets(server.DataDir);
|
||||
LoadAssets(UploadDir);
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.TransferRequest, new PacketCallback(TransferRequestHandler));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
@@ -93,290 +86,6 @@ namespace Simian.Extensions
|
||||
return AssetStore.TryGetValue(id, out asset);
|
||||
}
|
||||
|
||||
#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
|
||||
StoreAsset(asset);
|
||||
|
||||
// Send a success response
|
||||
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
|
||||
complete.AssetBlock.Success = true;
|
||||
complete.AssetBlock.Type = request.AssetBlock.Type;
|
||||
complete.AssetBlock.UUID = assetID;
|
||||
server.UDP.SendPacket(agent.AgentID, 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 = new byte[0];
|
||||
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;
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
server.UDP.SendPacket(agent.AgentID, 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);
|
||||
|
||||
StoreAsset(asset);
|
||||
|
||||
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
|
||||
complete.AssetBlock.Success = true;
|
||||
complete.AssetBlock.Type = (sbyte)asset.AssetType;
|
||||
complete.AssetBlock.UUID = asset.AssetID;
|
||||
server.UDP.SendPacket(agent.AgentID, 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 (AssetStore.TryGetValue(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
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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
|
||||
|
||||
void SaveAsset(Asset asset)
|
||||
{
|
||||
try
|
||||
|
||||
@@ -23,15 +23,15 @@ namespace Simian.Extensions
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AvatarPropertiesRequest, new PacketCallback(AvatarPropertiesRequestHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentWearablesRequest, new PacketCallback(AgentWearablesRequestHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentIsNowWearing, new PacketCallback(AgentIsNowWearingHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentSetAppearance, new PacketCallback(AgentSetAppearanceHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentCachedTexture, new PacketCallback(AgentCachedTextureHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentAnimation, new PacketCallback(AgentAnimationHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.SoundTrigger, new PacketCallback(SoundTriggerHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.ViewerEffect, new PacketCallback(ViewerEffectHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.UUIDNameRequest, new PacketCallback(UUIDNameRequestHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AvatarPropertiesRequest, AvatarPropertiesRequestHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentWearablesRequest, AgentWearablesRequestHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentIsNowWearing, AgentIsNowWearingHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentSetAppearance, AgentSetAppearanceHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentCachedTexture, AgentCachedTextureHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentAnimation, AgentAnimationHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.SoundTrigger, SoundTriggerHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ViewerEffect, ViewerEffectHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.UUIDNameRequest, UUIDNameRequestHandler);
|
||||
|
||||
if (CoarseLocationTimer != null) CoarseLocationTimer.Dispose();
|
||||
CoarseLocationTimer = new Timer(CoarseLocationTimer_Elapsed);
|
||||
|
||||
@@ -15,6 +15,7 @@ namespace Simian.Extensions
|
||||
GridClient client;
|
||||
PeriscopeImageDelivery imageDelivery;
|
||||
PeriscopeMovement movement;
|
||||
PeriscopeTransferManager transferManager;
|
||||
object loginLock = new object();
|
||||
|
||||
public Periscope()
|
||||
@@ -30,13 +31,13 @@ namespace Simian.Extensions
|
||||
client.Settings.MULTIPLE_SIMS = false;
|
||||
client.Settings.SEND_AGENT_UPDATES = false;
|
||||
|
||||
client.Network.OnCurrentSimChanged += new NetworkManager.CurrentSimChangedCallback(Network_OnCurrentSimChanged);
|
||||
client.Objects.OnNewPrim += new OpenMetaverse.ObjectManager.NewPrimCallback(Objects_OnNewPrim);
|
||||
client.Objects.OnNewAvatar += new OpenMetaverse.ObjectManager.NewAvatarCallback(Objects_OnNewAvatar);
|
||||
client.Objects.OnNewAttachment += new OpenMetaverse.ObjectManager.NewAttachmentCallback(Objects_OnNewAttachment);
|
||||
client.Objects.OnObjectKilled += new OpenMetaverse.ObjectManager.KillObjectCallback(Objects_OnObjectKilled);
|
||||
client.Objects.OnObjectUpdated += new OpenMetaverse.ObjectManager.ObjectUpdatedCallback(Objects_OnObjectUpdated);
|
||||
client.Avatars.OnAvatarAppearance += new OpenMetaverse.AvatarManager.AvatarAppearanceCallback(Avatars_OnAvatarAppearance);
|
||||
client.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);
|
||||
@@ -45,13 +46,20 @@ namespace Simian.Extensions
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentUpdate, AgentUpdateHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ChatFromViewer, ChatFromViewerHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectGrab, ObjectGrabHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectGrabUpdate, ObjectGrabUpdateHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ObjectDeGrab, ObjectDeGrabHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.ViewerEffect, ViewerEffectHandler);
|
||||
server.UDP.RegisterPacketCallback(PacketType.AgentAnimation, AgentAnimationHandler);
|
||||
|
||||
imageDelivery = new PeriscopeImageDelivery(server, client);
|
||||
movement = new PeriscopeMovement(server, this);
|
||||
transferManager = new PeriscopeTransferManager(server, client);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
transferManager.Stop();
|
||||
movement.Stop();
|
||||
imageDelivery.Stop();
|
||||
|
||||
@@ -62,6 +70,8 @@ namespace Simian.Extensions
|
||||
void Objects_OnNewPrim(Simulator simulator, Primitive prim, ulong regionHandle, ushort timeDilation)
|
||||
{
|
||||
SimulationObject simObj = new SimulationObject(prim, server);
|
||||
if (MasterAgent != null)
|
||||
simObj.Prim.OwnerID = MasterAgent.AgentID;
|
||||
server.Scene.ObjectAdd(this, simObj, PrimFlags.None);
|
||||
}
|
||||
|
||||
@@ -244,7 +254,6 @@ namespace Simian.Extensions
|
||||
switch (messageParts[0])
|
||||
{
|
||||
case "/teleport":
|
||||
//string simName;
|
||||
float x, y, z;
|
||||
|
||||
if (messageParts.Length == 5 &&
|
||||
@@ -261,6 +270,40 @@ namespace Simian.Extensions
|
||||
server.Avatars.SendAlert(agent, "Usage: /teleport \"sim name\" x y z");
|
||||
}
|
||||
return;
|
||||
case "/stats":
|
||||
server.Avatars.SendAlert(agent, String.Format("Downloading textures: {0}, Queued textures: {1}",
|
||||
imageDelivery.Pipeline.CurrentCount, imageDelivery.Pipeline.QueuedCount));
|
||||
return;
|
||||
case "/nudemod":
|
||||
int count = 0;
|
||||
Dictionary<UUID, Agent> agents;
|
||||
lock (server.Agents)
|
||||
agents = new Dictionary<UUID, Agent>(server.Agents);
|
||||
|
||||
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;
|
||||
|
||||
server.Scene.AvatarAppearance(this, curAgent, curAgent.Avatar.Textures, curAgent.VisualParams);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
server.Avatars.SendAlert(agent, String.Format("Modified appearances for {0} avatar(s)", count));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,6 +316,12 @@ namespace Simian.Extensions
|
||||
client.Self.Chat(finalMessage, chat.ChatData.Channel, (ChatType)chat.ChatData.Type);
|
||||
}
|
||||
|
||||
static void EraseTexture(Avatar avatar, AppearanceManager.TextureIndex texture)
|
||||
{
|
||||
Primitive.TextureEntryFace face = avatar.Textures.FaceTextures[(int)texture];
|
||||
if (face != null) face.TextureID = UUID.Zero;
|
||||
}
|
||||
|
||||
void AgentUpdateHandler(Packet packet, Agent agent)
|
||||
{
|
||||
AgentUpdatePacket update = (AgentUpdatePacket)packet;
|
||||
@@ -308,13 +357,77 @@ namespace Simian.Extensions
|
||||
|
||||
if (MasterAgent == null || update.AgentData.AgentID == MasterAgent.AgentID)
|
||||
{
|
||||
// Forward AgentUpdate packets with the AgentID/SessionID set to the bots ID
|
||||
update.AgentData.AgentID = client.Self.AgentID;
|
||||
update.AgentData.SessionID = client.Self.SessionID;
|
||||
client.Network.SendPacket(update);
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectGrabHandler(Packet packet, Agent agent)
|
||||
{
|
||||
ObjectGrabPacket grab = (ObjectGrabPacket)packet;
|
||||
|
||||
if (MasterAgent == null || grab.AgentData.AgentID == MasterAgent.AgentID)
|
||||
{
|
||||
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.AgentID)
|
||||
{
|
||||
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.AgentID)
|
||||
{
|
||||
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.AgentID)
|
||||
{
|
||||
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.AgentID)
|
||||
{
|
||||
animation.AgentData.AgentID = client.Self.AgentID;
|
||||
animation.AgentData.SessionID = client.Self.SessionID;
|
||||
|
||||
client.Network.SendPacket(animation);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Simian client packet handlers
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@ namespace Simian.Extensions
|
||||
{
|
||||
public class PeriscopeImageDelivery
|
||||
{
|
||||
public TexturePipeline Pipeline;
|
||||
|
||||
Simian server;
|
||||
GridClient client;
|
||||
TexturePipeline pipeline;
|
||||
Dictionary<UUID, ImageDownload> currentDownloads = new Dictionary<UUID, ImageDownload>();
|
||||
|
||||
public PeriscopeImageDelivery(Simian server, GridClient client)
|
||||
@@ -19,15 +20,15 @@ namespace Simian.Extensions
|
||||
this.server = server;
|
||||
this.client = client;
|
||||
|
||||
pipeline = new TexturePipeline(client, 10);
|
||||
pipeline.OnDownloadFinished += new TexturePipeline.DownloadFinishedCallback(pipeline_OnDownloadFinished);
|
||||
Pipeline = new TexturePipeline(client, 12);
|
||||
Pipeline.OnDownloadFinished += new TexturePipeline.DownloadFinishedCallback(pipeline_OnDownloadFinished);
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.RequestImage, RequestImageHandler);
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
pipeline.Shutdown();
|
||||
Pipeline.Shutdown();
|
||||
}
|
||||
|
||||
void RequestImageHandler(Packet packet, Agent agent)
|
||||
@@ -54,7 +55,8 @@ namespace Simian.Extensions
|
||||
}
|
||||
else if (block.DiscardLevel == -1 && block.DownloadPriority == 0.0f)
|
||||
{
|
||||
// Aborting a download we are not tracking, ignore
|
||||
// Aborting a download we are not tracking, this may be in the pipeline
|
||||
Pipeline.AbortDownload(block.Image);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -73,7 +75,7 @@ namespace Simian.Extensions
|
||||
lock (currentDownloads)
|
||||
currentDownloads[block.Image] = download;
|
||||
|
||||
pipeline.RequestTexture(block.Image, (ImageType)block.Type);
|
||||
Pipeline.RequestTexture(block.Image, (ImageType)block.Type);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,10 +92,10 @@ namespace Simian.Extensions
|
||||
if (success)
|
||||
{
|
||||
// Set the texture to the downloaded texture data
|
||||
AssetTexture texture = new AssetTexture(id, pipeline.GetTextureToRender(id).AssetData);
|
||||
AssetTexture texture = new AssetTexture(id, Pipeline.GetTextureToRender(id).AssetData);
|
||||
download.Texture = texture;
|
||||
|
||||
pipeline.RemoveFromPipeline(id);
|
||||
Pipeline.RemoveFromPipeline(id);
|
||||
|
||||
// Store this texture in the local asset store for later
|
||||
server.Assets.StoreAsset(texture);
|
||||
|
||||
407
Programs/Simian/Extensions/PeriscopeTransferManager.cs
Normal file
407
Programs/Simian/Extensions/PeriscopeTransferManager.cs
Normal 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.Extensions
|
||||
{
|
||||
public class PeriscopeTransferManager
|
||||
{
|
||||
public const string UPLOAD_DIR = "uploadedAssets";
|
||||
|
||||
Simian server;
|
||||
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(Simian server, GridClient client)
|
||||
{
|
||||
this.server = server;
|
||||
this.client = client;
|
||||
|
||||
client.Assets.OnAssetReceived += new OpenMetaverse.AssetManager.AssetReceivedCallback(Assets_OnAssetReceived);
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler));
|
||||
server.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
|
||||
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;
|
||||
server.UDP.SendPacket(agent.AgentID, 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 = new byte[0];
|
||||
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;
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
server.UDP.SendPacket(agent.AgentID, 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);
|
||||
|
||||
server.Assets.StoreAsset(asset);
|
||||
|
||||
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
|
||||
complete.AssetBlock.Success = true;
|
||||
complete.AssetBlock.Type = (sbyte)asset.AssetType;
|
||||
complete.AssetBlock.UUID = asset.AssetID;
|
||||
server.UDP.SendPacket(agent.AgentID, 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 (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.AgentID, 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
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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 (server.Agents.TryGetValue(kvp.Key, out agent))
|
||||
{
|
||||
if (transfer.Success)
|
||||
{
|
||||
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;
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
358
Programs/Simian/Extensions/TransferManager.cs
Normal file
358
Programs/Simian/Extensions/TransferManager.cs
Normal file
@@ -0,0 +1,358 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using ExtensionLoader;
|
||||
using OpenMetaverse;
|
||||
using OpenMetaverse.Packets;
|
||||
|
||||
namespace Simian.Extensions
|
||||
{
|
||||
public class TransferManager : IExtension<Simian>
|
||||
{
|
||||
Simian server;
|
||||
Dictionary<ulong, Asset> CurrentUploads = new Dictionary<ulong, Asset>();
|
||||
|
||||
public TransferManager()
|
||||
{
|
||||
}
|
||||
|
||||
public void Start(Simian server)
|
||||
{
|
||||
this.server = server;
|
||||
|
||||
server.UDP.RegisterPacketCallback(PacketType.AssetUploadRequest, new PacketCallback(AssetUploadRequestHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.SendXferPacket, new PacketCallback(SendXferPacketHandler));
|
||||
server.UDP.RegisterPacketCallback(PacketType.AbortXfer, new PacketCallback(AbortXferHandler));
|
||||
server.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
|
||||
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;
|
||||
server.UDP.SendPacket(agent.AgentID, 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 = new byte[0];
|
||||
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;
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
server.UDP.SendPacket(agent.AgentID, 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);
|
||||
|
||||
server.Assets.StoreAsset(asset);
|
||||
|
||||
AssetUploadCompletePacket complete = new AssetUploadCompletePacket();
|
||||
complete.AssetBlock.Success = true;
|
||||
complete.AssetBlock.Type = (sbyte)asset.AssetType;
|
||||
complete.AssetBlock.UUID = asset.AssetID;
|
||||
server.UDP.SendPacket(agent.AgentID, 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 (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
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
|
||||
server.UDP.SendPacket(agent.AgentID, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user