From 5e4d6cab00cb29cd088ab7b62ab13aff103b64cb Mon Sep 17 00:00:00 2001 From: onefang Date: Sun, 19 May 2019 21:24:15 +1000 Subject: Dump OpenSim 0.9.0.1 into it's own branch. --- .../AssetTransaction/AgentAssetsTransactions.cs | 30 +- .../AssetTransaction/AssetTransactionModule.cs | 12 +- .../Agent/AssetTransaction/AssetXferUploader.cs | 295 ++++++++++++++--- .../Region/CoreModules/Agent/IPBan/IPBanModule.cs | 8 +- .../Region/CoreModules/Agent/IPBan/SceneBanner.cs | 4 +- .../Agent/TextureSender/J2KDecoderModule.cs | 11 +- .../Region/CoreModules/Agent/Xfer/XferModule.cs | 365 +++++++++++++++------ 7 files changed, 570 insertions(+), 155 deletions(-) (limited to 'OpenSim/Region/CoreModules/Agent') diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs index f56d17d..f48710f 100644 --- a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs +++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AgentAssetsTransactions.cs @@ -127,7 +127,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction if (estateModule.IsTerrainXfer(xferID)) return; } - + m_log.ErrorFormat( "[AGENT ASSET TRANSACTIONS]: Could not find uploader for xfer id {0}, packet id {1}, data length {2}", xferID, packetID, data.Length); @@ -152,7 +152,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction } } - public void RequestCreateInventoryItem(IClientAPI remoteClient, + public bool RequestCreateInventoryItem(IClientAPI remoteClient, UUID transactionID, UUID folderID, uint callbackID, string description, string name, sbyte invType, sbyte type, byte wearableType, uint nextOwnerMask) @@ -162,6 +162,8 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction uploader.RequestCreateInventoryItem( remoteClient, folderID, callbackID, description, name, invType, type, wearableType, nextOwnerMask); + + return true; } public void RequestUpdateTaskInventoryItem(IClientAPI remoteClient, @@ -170,6 +172,17 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction { AssetXferUploader uploader = RequestXferUploader(transactionID); + // Here we need to get the old asset to extract the + // texture UUIDs if it's a wearable. + if (item.Type == (int)AssetType.Bodypart || + item.Type == (int)AssetType.Clothing || + item.Type == (int)CustomAssetType.AnimationSet) + { + AssetBase oldAsset = m_Scene.AssetService.Get(item.AssetID.ToString()); + if (oldAsset != null) + uploader.SetOldData(oldAsset.Data); + } + uploader.RequestUpdateTaskInventoryItem(remoteClient, item); } @@ -178,7 +191,18 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction { AssetXferUploader uploader = RequestXferUploader(transactionID); + // Here we need to get the old asset to extract the + // texture UUIDs if it's a wearable. + if (item.AssetType == (int)AssetType.Bodypart || + item.AssetType == (int)AssetType.Clothing || + item.AssetType == (int)CustomAssetType.AnimationSet) + { + AssetBase oldAsset = m_Scene.AssetService.Get(item.AssetID.ToString()); + if (oldAsset != null) + uploader.SetOldData(oldAsset.Data); + } + uploader.RequestUpdateInventoryItem(remoteClient, item); } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs index b67c0df..7d9f3b3 100644 --- a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs +++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetTransactionModule.cs @@ -43,7 +43,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction IAgentAssetTransactions { // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - + protected Scene m_Scene; private bool m_dumpAssetsToFile = false; private int m_levelUpload = 0; @@ -53,7 +53,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction /// private Dictionary AgentTransactions = new Dictionary(); - + #region Region Module interface public void Initialise(IConfigSource source) @@ -158,7 +158,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction /// /// /// - public void HandleItemCreationFromTransaction(IClientAPI remoteClient, + public bool HandleItemCreationFromTransaction(IClientAPI remoteClient, UUID transactionID, UUID folderID, uint callbackID, string description, string name, sbyte invType, sbyte type, byte wearableType, uint nextOwnerMask) @@ -169,7 +169,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction AgentAssetTransactions transactions = GetUserTransactions(remoteClient.AgentId); - transactions.RequestCreateInventoryItem(remoteClient, transactionID, + return transactions.RequestCreateInventoryItem(remoteClient, transactionID, folderID, callbackID, description, name, invType, type, wearableType, nextOwnerMask); } @@ -241,7 +241,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction // m_log.DebugFormat( // "[ASSET TRANSACTION MODULE]: HandleUDPUploadRequest - assetID: {0}, transaction {1}, type {2}, storeLocal {3}, tempFile {4}, data.Length {5}", // assetID, transactionID, type, storeLocal, tempFile, data.Length); - + if (((AssetType)type == AssetType.Texture || (AssetType)type == AssetType.Sound || (AssetType)type == AssetType.TextureTGA || @@ -255,7 +255,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction // check user level if (avatar != null) { - if (avatar.UserLevel < m_levelUpload) + if (avatar.GodController.UserLevel < m_levelUpload) { remoteClient.SendAgentAlertMessage("Unable to upload asset. Insufficient permissions.", false); return; diff --git a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs index 5143204..52b9d0e 100644 --- a/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs +++ b/OpenSim/Region/CoreModules/Agent/AssetTransaction/AssetXferUploader.cs @@ -28,6 +28,7 @@ using System; using System.IO; using System.Reflection; +using System.Collections.Generic; using log4net; using OpenMetaverse; using OpenSim.Framework; @@ -40,6 +41,23 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction { public class AssetXferUploader { + + private List defaultIDs = new List { + // Viewer's notion of the default texture + new UUID("5748decc-f629-461c-9a36-a35a221fe21f"), // others == default blank + new UUID("7ca39b4c-bd19-4699-aff7-f93fd03d3e7b"), // hair + new UUID("6522e74d-1660-4e7f-b601-6f48c1659a77"), // eyes + new UUID("c228d1cf-4b5d-4ba8-84f4-899a0796aa97"), // skin + new UUID("8dcd4a48-2d37-4909-9f78-f7a9eb4ef903"), // transparency for alpha + // opensim assets textures possibly obsolete now + new UUID("00000000-0000-1111-9999-000000000010"), + new UUID("00000000-0000-1111-9999-000000000011"), + new UUID("00000000-0000-1111-9999-000000000012"), + // other transparency defined in assets + new UUID("3a367d1c-bef1-6d43-7595-e88c1e3aadb3"), + new UUID("1578a2b1-5179-4b53-b618-fe00ca5a5594"), + }; + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); /// @@ -87,6 +105,7 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction private sbyte type = 0; private byte wearableType = 0; + private byte[] m_oldData = null; public ulong XferID; private Scene m_Scene; @@ -129,18 +148,27 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction if (XferID == xferID) { - if (m_asset.Data.Length > 1) - { - byte[] destinationArray = new byte[m_asset.Data.Length + data.Length]; - Array.Copy(m_asset.Data, 0, destinationArray, 0, m_asset.Data.Length); - Array.Copy(data, 0, destinationArray, m_asset.Data.Length, data.Length); - m_asset.Data = destinationArray; - } - else + lock (this) { - byte[] buffer2 = new byte[data.Length - 4]; - Array.Copy(data, 4, buffer2, 0, data.Length - 4); - m_asset.Data = buffer2; + int assetLength = m_asset.Data.Length; + int dataLength = data.Length; + + if (m_asset.Data.Length > 1) + { + byte[] destinationArray = new byte[assetLength + dataLength]; + Array.Copy(m_asset.Data, 0, destinationArray, 0, assetLength); + Array.Copy(data, 0, destinationArray, assetLength, dataLength); + m_asset.Data = destinationArray; + } + else + { + if (dataLength > 4) + { + byte[] buffer2 = new byte[dataLength - 4]; + Array.Copy(data, 4, buffer2, 0, dataLength - 4); + m_asset.Data = buffer2; + } + } } ourClient.SendConfirmXfer(xferID, packetID); @@ -230,24 +258,24 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction { m_uploadState = UploadState.Complete; - ourClient.SendAssetUploadCompleteMessage(m_asset.Type, true, m_asset.FullID); - + bool sucess = true; if (m_createItem) { - CompleteCreateItem(m_createItemCallback); + sucess = CompleteCreateItem(m_createItemCallback); } else if (m_updateItem) { - CompleteItemUpdate(m_updateItemData); + sucess = CompleteItemUpdate(m_updateItemData); } else if (m_updateTaskItem) { - CompleteTaskItemUpdate(m_updateTaskItemData); + sucess = CompleteTaskItemUpdate(m_updateTaskItemData); } -// else if (m_storeLocal) -// { -// m_Scene.AssetService.Store(m_asset); -// } + else if (m_asset.Local) + { + m_Scene.AssetService.Store(m_asset); + } + ourClient.SendAssetUploadCompleteMessage(m_asset.Type, sucess, m_asset.FullID); } m_log.DebugFormat( @@ -319,14 +347,16 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction m_asset.Description = item.Description; m_asset.Type = (sbyte)item.AssetType; - if (m_asset.FullID != UUID.Zero) - { + // remove redundante m_Scene.InventoryService.UpdateItem + // if uploadState == UploadState.Complete) +// if (m_asset.FullID != UUID.Zero) +// { // We must always store the item at this point even if the asset hasn't finished uploading, in order // to avoid a race condition when the appearance module retrieves the item to set the asset id in // the AvatarAppearance structure. - item.AssetID = m_asset.FullID; - m_Scene.InventoryService.UpdateItem(item); - } +// item.AssetID = m_asset.FullID; +// m_Scene.InventoryService.UpdateItem(item); +// } if (m_uploadState == UploadState.Complete) { @@ -334,10 +364,21 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction } else { -// m_log.DebugFormat( -// "[ASSET XFER UPLOADER]: Holding update inventory item request {0} for {1} pending completion of asset xfer for transaction {2}", -// item.Name, remoteClient.Name, transactionID); - + // do it here to avoid the eventual race condition + if (m_asset.FullID != UUID.Zero) + { + // We must always store the item at this point even if the asset hasn't finished uploading, in order + // to avoid a race condition when the appearance module retrieves the item to set the asset id in + // the AvatarAppearance structure. + item.AssetID = m_asset.FullID; + m_Scene.InventoryService.UpdateItem(item); + } + + + // m_log.DebugFormat( + // "[ASSET XFER UPLOADER]: Holding update inventory item request {0} for {1} pending completion of asset xfer for transaction {2}", + // item.Name, remoteClient.Name, transactionID); + m_updateItem = true; m_updateItemData = item; } @@ -370,36 +411,70 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction /// Store the asset for the given item when it has been uploaded. /// /// - private void CompleteItemUpdate(InventoryItemBase item) + private bool CompleteItemUpdate(InventoryItemBase item) { // m_log.DebugFormat( // "[ASSET XFER UPLOADER]: Storing asset {0} for earlier item update for {1} for {2}", // m_asset.FullID, item.Name, ourClient.Name); - m_Scene.AssetService.Store(m_asset); - - m_transactions.RemoveXferUploader(m_transactionID); + uint perms = ValidateAssets(); + if(perms == 0) + { + string error = string.Format("Not enough permissions on asset(s) referenced by item '{0}', update failed", item.Name); + ourClient.SendAlertMessage(error); + m_transactions.RemoveXferUploader(m_transactionID); + ourClient.SendBulkUpdateInventory(item); // invalid the change item on viewer cache + } + else + { + m_Scene.AssetService.Store(m_asset); + if (m_asset.FullID != UUID.Zero) + { + item.AssetID = m_asset.FullID; + m_Scene.InventoryService.UpdateItem(item); + } + ourClient.SendInventoryItemCreateUpdate(item, m_transactionID, 0); + m_transactions.RemoveXferUploader(m_transactionID); + m_Scene.EventManager.TriggerOnNewInventoryItemUploadComplete(item, 0); + } - m_Scene.EventManager.TriggerOnNewInventoryItemUploadComplete(ourClient.AgentId, (AssetType)type, m_asset.FullID, m_asset.Name, 0); + return perms != 0; } /// /// Store the asset for the given task item when it has been uploaded. /// /// - private void CompleteTaskItemUpdate(TaskInventoryItem taskItem) + private bool CompleteTaskItemUpdate(TaskInventoryItem taskItem) { // m_log.DebugFormat( // "[ASSET XFER UPLOADER]: Storing asset {0} for earlier task item update for {1} for {2}", // m_asset.FullID, taskItem.Name, ourClient.Name); - m_Scene.AssetService.Store(m_asset); + if(ValidateAssets() == 0) + { + m_transactions.RemoveXferUploader(m_transactionID); + string error = string.Format("Not enough permissions on asset(s) referenced by task item '{0}', update failed", taskItem.Name); + ourClient.SendAlertMessage(error); + // force old asset to viewers ?? + return false; + } + m_Scene.AssetService.Store(m_asset); m_transactions.RemoveXferUploader(m_transactionID); + return true; } - private void CompleteCreateItem(uint callbackID) + private bool CompleteCreateItem(uint callbackID) { + if(ValidateAssets() == 0) + { + m_transactions.RemoveXferUploader(m_transactionID); + string error = string.Format("Not enough permissions on asset(s) referenced by item '{0}', creation failed", m_name); + ourClient.SendAlertMessage(error); + return false; + } + m_Scene.AssetService.Store(m_asset); InventoryItemBase item = new InventoryItemBase(); @@ -420,13 +495,155 @@ namespace OpenSim.Region.CoreModules.Agent.AssetTransaction item.Flags = (uint) wearableType; item.CreationDate = Util.UnixTimeSinceEpoch(); + m_log.DebugFormat("[XFER]: Created item {0} with asset {1}", + item.ID, item.AssetID); + if (m_Scene.AddInventoryItem(item)) - ourClient.SendInventoryItemCreateUpdate(item, callbackID); + ourClient.SendInventoryItemCreateUpdate(item, m_transactionID, callbackID); else ourClient.SendAlertMessage("Unable to create inventory item"); m_transactions.RemoveXferUploader(m_transactionID); + return true; + } + + private uint ValidateAssets() + { + uint retPerms = 0x7fffffff; +// if(m_Scene.Permissions.BypassPermissions()) +// return retPerms; + + if (m_asset.Type == (sbyte)CustomAssetType.AnimationSet) + { + + AnimationSet animSet = new AnimationSet(m_asset.Data); + + retPerms &= animSet.Validate(x => { + const uint required = (uint)(PermissionMask.Transfer | PermissionMask.Copy); + uint perms = (uint)m_Scene.InventoryService.GetAssetPermissions(ourClient.AgentId, x); + // currrent yes/no rule + if ((perms & required) != required) + return 0; + return perms; + }); + + return retPerms; + } + + if (m_asset.Type == (sbyte)AssetType.Clothing || + m_asset.Type == (sbyte)AssetType.Bodypart) + { + const uint texturesfullPermMask = (uint)(PermissionMask.Modify | PermissionMask.Transfer | PermissionMask.Copy); + string content = System.Text.Encoding.ASCII.GetString(m_asset.Data); + string[] lines = content.Split(new char[] {'\n'}); + + // on current requiriment of full rigths assume old assets where accepted + Dictionary allowed = ExtractTexturesFromOldData(); + + int textures = 0; + + foreach (string line in lines) + { + try + { + if (line.StartsWith("textures ")) + textures = Convert.ToInt32(line.Substring(9)); + + else if (textures > 0) + { + string[] parts = line.Split(new char[] {' '}); + + UUID tx = new UUID(parts[1]); + int id = Convert.ToInt32(parts[0]); + + if (defaultIDs.Contains(tx) || tx == UUID.Zero || + (allowed.ContainsKey(id) && allowed[id] == tx)) + { + continue; + } + else + { + uint perms = (uint)m_Scene.InventoryService.GetAssetPermissions(ourClient.AgentId, tx); + + if ((perms & texturesfullPermMask) != texturesfullPermMask) + { + m_log.ErrorFormat("[ASSET UPLOADER]: REJECTED update with texture {0} from {1} because they do not own the texture", tx, ourClient.AgentId); + return 0; + } + else + { + retPerms &= perms; + } + } + textures--; + } + } + catch + { + // If it's malformed, skip it + } + } + } + return retPerms; + } + +/* not in use + /// + /// Get the asset data uploaded in this transfer. + /// + /// null if the asset has not finished uploading + public AssetBase GetAssetData() + { + if (m_uploadState == UploadState.Complete) + { + ValidateAssets(); + return m_asset; + } + + return null; + } +*/ + public void SetOldData(byte[] d) + { + m_oldData = d; } + private Dictionary ExtractTexturesFromOldData() + { + Dictionary result = new Dictionary(); + if (m_oldData == null) + return result; + + string content = System.Text.Encoding.ASCII.GetString(m_oldData); + string[] lines = content.Split(new char[] {'\n'}); + + int textures = 0; + + foreach (string line in lines) + { + try + { + if (line.StartsWith("textures ")) + { + textures = Convert.ToInt32(line.Substring(9)); + } + else if (textures > 0) + { + string[] parts = line.Split(new char[] {' '}); + + UUID tx = new UUID(parts[1]); + int id = Convert.ToInt32(parts[0]); + result[id] = tx; + textures--; + } + } + catch + { + // If it's malformed, skip it + } + } + + return result; + } } -} \ No newline at end of file +} diff --git a/OpenSim/Region/CoreModules/Agent/IPBan/IPBanModule.cs b/OpenSim/Region/CoreModules/Agent/IPBan/IPBanModule.cs index 4b457b1..ff71593 100644 --- a/OpenSim/Region/CoreModules/Agent/IPBan/IPBanModule.cs +++ b/OpenSim/Region/CoreModules/Agent/IPBan/IPBanModule.cs @@ -38,7 +38,7 @@ using OpenSim.Region.Framework.Scenes; namespace OpenSim.Region.CoreModules.Agent.IPBan { [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "IPBanModule")] - public class IPBanModule : ISharedRegionModule + public class IPBanModule : ISharedRegionModule { #region Implementation of ISharedRegionModule @@ -86,7 +86,7 @@ namespace OpenSim.Region.CoreModules.Agent.IPBan public void Close() { - + } public string Name @@ -107,9 +107,9 @@ namespace OpenSim.Region.CoreModules.Agent.IPBan /// matching domain (including "betasomewhere.com", "beta.somewhere.com", /// "somewhere.com.beta") - make sure to be reasonably specific in DNS /// bans. - /// + /// /// IP address bans match on first characters, so, - /// "127.0.0.1" will ban only that address, + /// "127.0.0.1" will ban only that address, /// "127.0.1" will ban "127.0.10.0" /// but "127.0.1." will ban only the "127.0.1.*" network /// diff --git a/OpenSim/Region/CoreModules/Agent/IPBan/SceneBanner.cs b/OpenSim/Region/CoreModules/Agent/IPBan/SceneBanner.cs index 8502006..b4c68e2 100644 --- a/OpenSim/Region/CoreModules/Agent/IPBan/SceneBanner.cs +++ b/OpenSim/Region/CoreModules/Agent/IPBan/SceneBanner.cs @@ -53,9 +53,9 @@ namespace OpenSim.Region.CoreModules.Agent.IPBan if (bans.Count > 0) { IClientIPEndpoint ipEndpoint; - if (client.TryGet(out ipEndpoint)) + if (client.TryGet(out ipEndpoint) && ipEndpoint.RemoteEndPoint != null) { - IPAddress end = ipEndpoint.EndPoint; + IPAddress end = ipEndpoint.RemoteEndPoint.Address; try { diff --git a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs index 47dcbcd..6e4a710 100644 --- a/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs +++ b/OpenSim/Region/CoreModules/Agent/TextureSender/J2KDecoderModule.cs @@ -57,13 +57,13 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender /// List of client methods to notify of results of decode private readonly Dictionary> m_notifyList = new Dictionary>(); /// Cache that will store decoded JPEG2000 layer boundary data - private IImprovedAssetCache m_cache; - private IImprovedAssetCache Cache + private IAssetCache m_cache; + private IAssetCache Cache { get { if (m_cache == null) - m_cache = m_scene.RequestModuleInterface(); + m_cache = m_scene.RequestModuleInterface(); return m_cache; } @@ -285,7 +285,7 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender // Cache Decoded layers SaveFileCacheForAsset(assetID, layers); } - + // Notify Interested Parties lock (m_notifyList) { @@ -369,7 +369,8 @@ namespace OpenSim.Region.CoreModules.Agent.TextureSender else if (Cache != null) { string assetName = "j2kCache_" + AssetId.ToString(); - AssetBase layerDecodeAsset = Cache.Get(assetName); + AssetBase layerDecodeAsset; + Cache.Get(assetName, out layerDecodeAsset); if (layerDecodeAsset != null) { diff --git a/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs b/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs index 4299726..1b6401a 100644 --- a/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs +++ b/OpenSim/Region/CoreModules/Agent/Xfer/XferModule.cs @@ -28,10 +28,12 @@ using System; using System.Collections.Generic; using System.Reflection; +using System.Threading; using Nini.Config; using log4net; using OpenMetaverse; using OpenSim.Framework; +using OpenSim.Framework.Monitoring; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; @@ -45,9 +47,13 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer private Scene m_scene; private Dictionary NewFiles = new Dictionary(); private Dictionary Transfers = new Dictionary(); - private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private object timeTickLock = new object(); + private double lastTimeTick = 0.0; + private double lastFilesExpire = 0.0; + private bool inTimeTick = false; + public struct XferRequest { public IClientAPI remoteClient; @@ -59,26 +65,30 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer private class FileData { public byte[] Data; - public int Count; + public int refsCount; + public double timeStampMS; } - + #region INonSharedRegionModule Members public void Initialise(IConfigSource config) { + lastTimeTick = Util.GetTimeStampMS() + 30000.0; + lastFilesExpire = lastTimeTick + 180000.0; } public void AddRegion(Scene scene) { m_scene = scene; - m_scene.EventManager.OnNewClient += NewClient; - m_scene.RegisterModuleInterface(this); + m_scene.EventManager.OnNewClient += NewClient; + m_scene.EventManager.OnRegionHeartbeatEnd += OnTimeTick; } public void RemoveRegion(Scene scene) { m_scene.EventManager.OnNewClient -= NewClient; + m_scene.EventManager.OnRegionHeartbeatEnd -= OnTimeTick; m_scene.UnregisterModuleInterface(this); m_scene = null; @@ -104,6 +114,41 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer #endregion + public void OnTimeTick(Scene scene) + { + // we are on a heartbeat thread we there can be several + if(Monitor.TryEnter(timeTickLock)) + { + if(!inTimeTick) + { + double now = Util.GetTimeStampMS(); + if(now - lastTimeTick > 1750.0) + { + + if(Transfers.Count == 0 && NewFiles.Count == 0) + lastTimeTick = now; + else + { + inTimeTick = true; + + //don't overload busy heartbeat + WorkManager.RunInThreadPool( + delegate + { + transfersTimeTick(now); + expireFiles(now); + + lastTimeTick = now; + inTimeTick = false; + }, + null, + "XferTimeTick"); + } + } + } + Monitor.Exit(timeTickLock); + } + } #region IXfer Members /// @@ -118,24 +163,45 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer { lock (NewFiles) { + double now = Util.GetTimeStampMS(); if (NewFiles.ContainsKey(fileName)) { - NewFiles[fileName].Count++; + NewFiles[fileName].refsCount++; NewFiles[fileName].Data = data; + NewFiles[fileName].timeStampMS = now; } else { FileData fd = new FileData(); - fd.Count = 1; + fd.refsCount = 1; fd.Data = data; + fd.timeStampMS = now; NewFiles.Add(fileName, fd); } } - return true; } #endregion + public void expireFiles(double now) + { + lock (NewFiles) + { + // hopefully we will not have many files so nasty code will do it + if(now - lastFilesExpire > 120000.0) + { + lastFilesExpire = now; + List expires = new List(); + foreach(string fname in NewFiles.Keys) + { + if(NewFiles[fname].refsCount == 0 && now - NewFiles[fname].timeStampMS > 120000.0) + expires.Add(fname); + } + foreach(string fname in expires) + NewFiles.Remove(fname); + } + } + } public void NewClient(IClientAPI client) { @@ -144,6 +210,51 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer client.OnAbortXfer += AbortXfer; } + public void OnClientClosed(IClientAPI client) + { + client.OnRequestXfer -= RequestXfer; + client.OnConfirmXfer -= AckPacket; + client.OnAbortXfer -= AbortXfer; + } + + private void RemoveOrDecrementFile(string fileName) + { + // NewFiles must be locked + + if (NewFiles.ContainsKey(fileName)) + { + if (NewFiles[fileName].refsCount == 1) + NewFiles.Remove(fileName); + else + NewFiles[fileName].refsCount--; + } + } + + public void transfersTimeTick(double now) + { + XferDownLoad[] xfrs; + lock(Transfers) + { + if(Transfers.Count == 0) + return; + + xfrs = new XferDownLoad[Transfers.Count]; + Transfers.Values.CopyTo(xfrs,0); + } + foreach(XferDownLoad xfr in xfrs) + { + if(xfr.checkTime(now)) + { + ulong xfrID = xfr.XferID; + lock(Transfers) + { + if(Transfers.ContainsKey(xfrID)) + Transfers.Remove(xfrID); + } + } + } + } + /// /// /// @@ -156,78 +267,52 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer { if (NewFiles.ContainsKey(fileName)) { - if (!Transfers.ContainsKey(xferID)) + lock(Transfers) { - byte[] fileData = NewFiles[fileName].Data; - XferDownLoad transaction = new XferDownLoad(fileName, fileData, xferID, remoteClient); - - Transfers.Add(xferID, transaction); - - if (transaction.StartSend()) - RemoveXferData(xferID); - - // The transaction for this file is either complete or on its way - RemoveOrDecrement(fileName); - + if (!Transfers.ContainsKey(xferID)) + { + byte[] fileData = NewFiles[fileName].Data; + int burstSize = remoteClient.GetAgentThrottleSilent((int)ThrottleOutPacketType.Asset) >> 11; + if(Transfers.Count > 1) + burstSize /= Transfers.Count; + XferDownLoad transaction = + new XferDownLoad(fileName, fileData, xferID, remoteClient, burstSize); + + Transfers.Add(xferID, transaction); + + transaction.StartSend(); + + // The transaction for this file is on its way + RemoveOrDecrementFile(fileName); + } } } else m_log.WarnFormat("[Xfer]: {0} not found", fileName); - } } public void AckPacket(IClientAPI remoteClient, ulong xferID, uint packet) { - lock (NewFiles) // This is actually to lock Transfers + lock (Transfers) { if (Transfers.ContainsKey(xferID)) { - XferDownLoad dl = Transfers[xferID]; if (Transfers[xferID].AckPacket(packet)) - { - RemoveXferData(xferID); - RemoveOrDecrement(dl.FileName); - } + Transfers.Remove(xferID); } } } - private void RemoveXferData(ulong xferID) - { - // NewFiles must be locked! - if (Transfers.ContainsKey(xferID)) - { - XferModule.XferDownLoad xferItem = Transfers[xferID]; - //string filename = xferItem.FileName; - Transfers.Remove(xferID); - xferItem.Data = new byte[0]; // Clear the data - xferItem.DataPointer = 0; - - } - } - public void AbortXfer(IClientAPI remoteClient, ulong xferID) { - lock (NewFiles) + lock (Transfers) { if (Transfers.ContainsKey(xferID)) - RemoveOrDecrement(Transfers[xferID].FileName); - - RemoveXferData(xferID); - } - } - - private void RemoveOrDecrement(string fileName) - { - // NewFiles must be locked - - if (NewFiles.ContainsKey(fileName)) - { - if (NewFiles[fileName].Count == 1) - NewFiles.Remove(fileName); - else - NewFiles[fileName].Count--; + { + Transfers[xferID].done(); + Transfers.Remove(xferID); + } } } @@ -236,52 +321,124 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer public class XferDownLoad { public IClientAPI Client; - private bool complete; public byte[] Data = new byte[0]; - public int DataPointer = 0; public string FileName = String.Empty; - public uint Packet = 0; - public uint Serial = 1; public ulong XferID = 0; - - public XferDownLoad(string fileName, byte[] data, ulong xferID, IClientAPI client) + public bool isDeleted = false; + + private object myLock = new object(); + private double lastsendTimeMS; + private int LastPacket; + private int lastBytes; + private int lastSentPacket; + private int lastAckPacket; + private int burstSize; + private int retries = 0; + + public XferDownLoad(string fileName, byte[] data, ulong xferID, IClientAPI client, int burstsz) { FileName = fileName; Data = data; XferID = xferID; Client = client; + burstSize = burstsz; } public XferDownLoad() { } + public void done() + { + if(!isDeleted) + { + Data = new byte[0]; + isDeleted = true; + } + } + /// /// Start a transfer /// /// True if the transfer is complete, false if not - public bool StartSend() + public void StartSend() { - if (Data.Length < 1000) + lock(myLock) { - // for now (testing) we only support files under 1000 bytes - byte[] transferData = new byte[Data.Length + 4]; - Array.Copy(Utils.IntToBytes(Data.Length), 0, transferData, 0, 4); - Array.Copy(Data, 0, transferData, 4, Data.Length); - Client.SendXferPacket(XferID, 0 + 0x80000000, transferData); - complete = true; + if(Data.Length == 0) //?? + { + LastPacket = 0; + lastBytes = 0; + burstSize = 0; + } + else + { + // payload of 1024bytes + LastPacket = Data.Length >> 10; + lastBytes = Data.Length & 0x3ff; + if(lastBytes == 0) + { + lastBytes = 1024; + LastPacket--; + } + + } + + lastAckPacket = -1; + lastSentPacket = -1; + + double now = Util.GetTimeStampMS(); + + SendBurst(now); + return; + } + } + + private void SendBurst(double now) + { + int start = lastAckPacket + 1; + int end = start + burstSize; + if (end > LastPacket) + end = LastPacket; + while(start <= end) + SendPacket(start++ , now); + } + + private void SendPacket(int pkt, double now) + { + if(pkt > LastPacket) + return; + + int pktsize; + uint pktid; + if (pkt == LastPacket) + { + pktsize = lastBytes; + pktid = (uint)pkt | 0x80000000u; } else { - byte[] transferData = new byte[1000 + 4]; + pktsize = 1024; + pktid = (uint)pkt; + } + + byte[] transferData; + if(pkt == 0) + { + transferData = new byte[pktsize + 4]; Array.Copy(Utils.IntToBytes(Data.Length), 0, transferData, 0, 4); - Array.Copy(Data, 0, transferData, 4, 1000); - Client.SendXferPacket(XferID, 0, transferData); - Packet++; - DataPointer = 1000; + Array.Copy(Data, 0, transferData, 4, pktsize); } + else + { + transferData = new byte[pktsize]; + Array.Copy(Data, pkt << 10, transferData, 0, pktsize); + } + + Client.SendXferPacket(XferID, pktid, transferData, false); - return complete; + lastSentPacket = pkt; + lastsendTimeMS = now; } /// @@ -291,30 +448,46 @@ namespace OpenSim.Region.CoreModules.Agent.Xfer /// True if the transfer is complete, false otherwise public bool AckPacket(uint packet) { - if (!complete) + lock(myLock) { - if ((Data.Length - DataPointer) > 1000) + if(isDeleted) + return true; + + packet &= 0x7fffffff; + if(lastAckPacket < packet) + lastAckPacket = (int)packet; + + if(lastAckPacket == LastPacket) { - byte[] transferData = new byte[1000]; - Array.Copy(Data, DataPointer, transferData, 0, 1000); - Client.SendXferPacket(XferID, Packet, transferData); - Packet++; - DataPointer += 1000; + done(); + return true; } - else + double now = Util.GetTimeStampMS(); + SendPacket(lastSentPacket + 1, now); + return false; + } + } + + public bool checkTime(double now) + { + if(Monitor.TryEnter(myLock)) + { + if(!isDeleted) { - byte[] transferData = new byte[Data.Length - DataPointer]; - Array.Copy(Data, DataPointer, transferData, 0, Data.Length - DataPointer); - uint endPacket = Packet |= (uint) 0x80000000; - Client.SendXferPacket(XferID, endPacket, transferData); - Packet++; - DataPointer += (Data.Length - DataPointer); - - complete = true; + double timeMS = now - lastsendTimeMS; + if(timeMS > 60000.0) + done(); + else if(timeMS > 3500.0 && retries++ < 3) + { + burstSize >>= 1; + SendBurst(now); + } } - } - return complete; + Monitor.Exit(myLock); + return isDeleted; + } + return false; } } -- cgit v1.1