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. --- .../Linden/Caps/BunchOfCaps/BunchOfCaps.cs | 1610 ++++++++++++++++---- .../Linden/Caps/BunchOfCaps/BunchOfCapsModule.cs | 6 +- .../Linden/Caps/BunchOfCaps/MeshCost.cs | 746 +++++++++ 3 files changed, 2025 insertions(+), 337 deletions(-) create mode 100644 OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs (limited to 'OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps') diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs index 774202e..6f5775a 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCaps.cs @@ -26,11 +26,14 @@ */ using System; +using System.Timers; using System.Collections; using System.Collections.Generic; +using System.Collections.Specialized; using System.IO; using System.Reflection; using System.Text; +using System.Web; using OpenMetaverse; using OpenMetaverse.StructuredData; @@ -40,11 +43,11 @@ using log4net; using OpenSim.Framework; using OpenSim.Framework.Capabilities; using OpenSim.Region.Framework; +using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes.Serialization; using OpenSim.Framework.Servers; using OpenSim.Framework.Servers.HttpServer; -using OpenSim.Framework.Client; using OpenSim.Services.Interfaces; using Caps = OpenSim.Framework.Capabilities.Caps; @@ -55,14 +58,16 @@ using PermissionMask = OpenSim.Framework.PermissionMask; namespace OpenSim.Region.ClientStack.Linden { public delegate void UpLoadedAsset( - string assetName, string description, UUID assetID, UUID inventoryItem, UUID parentFolder, - byte[] data, string inventoryType, string assetType); + string assetName, string description, UUID assetID, UUID inventoryItem, UUID parentFolder, + byte[] data, string inventoryType, string assetType, + int cost, UUID texturesFolder, int nreqtextures, int nreqmeshs, int nreqinstances, + bool IsAtestUpload, ref string error, ref int nextOwnerMask, ref int groupMask, ref int everyoneMask); public delegate UUID UpdateItem(UUID itemID, byte[] data); public delegate void UpdateTaskScript(UUID itemID, UUID primID, bool isScriptRunning, byte[] data, ref ArrayList errors); - public delegate void NewInventoryItem(UUID userID, InventoryItemBase item); + public delegate void NewInventoryItem(UUID userID, InventoryItemBase item, uint cost); public delegate void NewAsset(AssetBase asset); @@ -87,21 +92,12 @@ namespace OpenSim.Region.ClientStack.Linden LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); private Scene m_Scene; + private UUID m_AgentID; private Caps m_HostCapsObj; + private ModelCost m_ModelCost; - private static readonly string m_requestPath = "0000/"; - // private static readonly string m_mapLayerPath = "0001/"; - private static readonly string m_newInventory = "0002/"; - //private static readonly string m_requestTexture = "0003/"; - private static readonly string m_notecardUpdatePath = "0004/"; - private static readonly string m_notecardTaskUpdatePath = "0005/"; - // private static readonly string m_fetchInventoryPath = "0006/"; - private static readonly string m_copyFromNotecardPath = "0007/"; // private static readonly string m_remoteParcelRequestPath = "0009/";// This is in the LandManagementModule. - private static readonly string m_getObjectPhysicsDataPath = "0101/"; - /* 0102 - 0103 RESERVED */ - private static readonly string m_UpdateAgentInformationPath = "0500/"; - + // These are callbacks which will be setup by the scene so that we can update scene data when we // receive capability calls public NewInventoryItem AddNewInventoryItem = null; @@ -115,12 +111,45 @@ namespace OpenSim.Region.ClientStack.Linden private IAssetService m_assetService; private bool m_dumpAssetsToFile = false; private string m_regionName; + private int m_levelUpload = 0; - public BunchOfCaps(Scene scene, Caps caps) + private bool m_enableFreeTestUpload = false; // allows "TEST-" prefix hack + private bool m_ForceFreeTestUpload = false; // forces all uploads to be test + + private bool m_enableModelUploadTextureToInventory = false; // place uploaded textures also in inventory + // may not be visible till relog + + private bool m_RestrictFreeTestUploadPerms = false; // reduces also the permitions. Needs a creator defined!! + private UUID m_testAssetsCreatorID = UUID.Zero; + + private float m_PrimScaleMin = 0.001f; + + private bool m_AllowCapHomeLocation = true; + private bool m_AllowCapGroupMemberData = true; + private IUserManagement m_UserManager; + + + private enum FileAgentInventoryState : int + { + idle = 0, + processRequest = 1, + waitUpload = 2, + processUpload = 3 + } + private FileAgentInventoryState m_FileAgentInventoryState = FileAgentInventoryState.idle; + + public BunchOfCaps(Scene scene, UUID agentID, Caps caps) { m_Scene = scene; + m_AgentID = agentID; m_HostCapsObj = caps; + + // create a model upload cost provider + m_ModelCost = new ModelCost(scene); + + m_PrimScaleMin = m_ModelCost.PrimScaleMin; + IConfigSource config = m_Scene.Config; if (config != null) { @@ -135,10 +164,45 @@ namespace OpenSim.Region.ClientStack.Linden { m_persistBakedTextures = appearanceConfig.GetBoolean("PersistBakedTextures", m_persistBakedTextures); } + // economy for model upload + IConfig EconomyConfig = config.Configs["Economy"]; + if (EconomyConfig != null) + { + m_ModelCost.Econfig(EconomyConfig); + + m_enableModelUploadTextureToInventory = EconomyConfig.GetBoolean("MeshModelAllowTextureToInventory", m_enableModelUploadTextureToInventory); + + m_RestrictFreeTestUploadPerms = EconomyConfig.GetBoolean("m_RestrictFreeTestUploadPerms", m_RestrictFreeTestUploadPerms); + m_enableFreeTestUpload = EconomyConfig.GetBoolean("AllowFreeTestUpload", m_enableFreeTestUpload); + m_ForceFreeTestUpload = EconomyConfig.GetBoolean("ForceFreeTestUpload", m_ForceFreeTestUpload); + string testcreator = EconomyConfig.GetString("TestAssetsCreatorID", ""); + if (testcreator != "") + { + UUID id; + UUID.TryParse(testcreator, out id); + if (id != null) + m_testAssetsCreatorID = id; + } + } + + IConfig CapsConfig = config.Configs["ClientStack.LindenCaps"]; + if (CapsConfig != null) + { + string homeLocationUrl = CapsConfig.GetString("Cap_HomeLocation", "localhost"); + if(homeLocationUrl == String.Empty) + m_AllowCapHomeLocation = false; + + string GroupMemberDataUrl = CapsConfig.GetString("Cap_GroupMemberData", "localhost"); + if(GroupMemberDataUrl == String.Empty) + m_AllowCapGroupMemberData = false; + } } m_assetService = m_Scene.AssetService; m_regionName = m_Scene.RegionInfo.RegionName; + m_UserManager = m_Scene.RequestModuleInterface(); + if (m_UserManager == null) + m_log.Error("[CAPS]: GetDisplayNames disabled because user management component not found"); RegisterHandlers(); @@ -146,6 +210,13 @@ namespace OpenSim.Region.ClientStack.Linden ItemUpdatedCall = m_Scene.CapsUpdateInventoryItemAsset; TaskScriptUpdatedCall = m_Scene.CapsUpdateTaskInventoryScriptAsset; GetClient = m_Scene.SceneGraph.GetControllingClient; + + m_FileAgentInventoryState = FileAgentInventoryState.idle; + } + + public string GetNewCapPath() + { + return "/CAPS/" + UUID.Random(); } /// @@ -153,33 +224,67 @@ namespace OpenSim.Region.ClientStack.Linden /// public void RegisterHandlers() { - string capsBase = "/CAPS/" + m_HostCapsObj.CapsObjectPath; + // this path is also defined elsewhere so keeping it + string seedcapsBase = "/CAPS/" + m_HostCapsObj.CapsObjectPath +"0000/"; - RegisterRegionServiceHandlers(capsBase); - RegisterInventoryServiceHandlers(capsBase); + // the root of all evil path needs to be capsBase + m_requestPath + m_HostCapsObj.RegisterHandler( + "SEED", new RestStreamHandler("POST", seedcapsBase, SeedCapRequest, "SEED", null)); + +// m_log.DebugFormat( +// "[CAPS]: Registered seed capability {0} for {1}", seedcapsBase, m_HostCapsObj.AgentID); + + RegisterRegionServiceHandlers(); + RegisterInventoryServiceHandlers(); + RegisterOtherHandlers(); } - public void RegisterRegionServiceHandlers(string capsBase) + public void RegisterRegionServiceHandlers() { try { - // the root of all evil - m_HostCapsObj.RegisterHandler( - "SEED", new RestStreamHandler("POST", capsBase + m_requestPath, SeedCapRequest, "SEED", null)); - -// m_log.DebugFormat( -// "[CAPS]: Registered seed capability {0} for {1}", capsBase + m_requestPath, m_HostCapsObj.AgentID); - //m_capsHandlers["MapLayer"] = // new LLSDStreamhandler("POST", - // capsBase + m_mapLayerPath, - // GetMapLayer); - IRequestHandler req - = new RestStreamHandler( - "POST", capsBase + m_notecardTaskUpdatePath, ScriptTaskInventory, "UpdateScript", null); + // GetNewCapPath(), + // GetMapLayer); + + IRequestHandler getObjectPhysicsDataHandler = new RestStreamHandler( + "POST", GetNewCapPath(), GetObjectPhysicsData, "GetObjectPhysicsData", null); + m_HostCapsObj.RegisterHandler("GetObjectPhysicsData", getObjectPhysicsDataHandler); + + IRequestHandler getObjectCostHandler = new RestStreamHandler( + "POST", GetNewCapPath(), GetObjectCost, "GetObjectCost", null ); + m_HostCapsObj.RegisterHandler("GetObjectCost", getObjectCostHandler); + IRequestHandler ResourceCostSelectedHandler = new RestStreamHandler( + "POST", GetNewCapPath(), ResourceCostSelected, "ResourceCostSelected", null); + m_HostCapsObj.RegisterHandler("ResourceCostSelected", ResourceCostSelectedHandler); + + IRequestHandler req = new RestStreamHandler( + "POST", GetNewCapPath(), ScriptTaskInventory, "UpdateScript", null); m_HostCapsObj.RegisterHandler("UpdateScriptTaskInventory", req); m_HostCapsObj.RegisterHandler("UpdateScriptTask", req); + + if(m_AllowCapHomeLocation) + { + IRequestHandler HomeLocationHandler = new RestStreamHandler( + "POST", GetNewCapPath(), HomeLocation, "HomeLocation", null); + m_HostCapsObj.RegisterHandler("HomeLocation", HomeLocationHandler); + } + + if(m_AllowCapGroupMemberData) + { + IRequestHandler GroupMemberDataHandler = new RestStreamHandler( + "POST", GetNewCapPath(), GroupMemberData, "GroupMemberData", null); + m_HostCapsObj.RegisterHandler("GroupMemberData", GroupMemberDataHandler); + } + + +// IRequestHandler animSetRequestHandler +// = new RestStreamHandler( +// "POST", capsBase + m_animSetTaskUpdatePath, AnimSetTaskInventory, "UpdateScript", null); + +// m_HostCapsObj.RegisterHandler("UpdateAnimSetTaskInventory", animSetRequestHandler); } catch (Exception e) { @@ -187,68 +292,29 @@ namespace OpenSim.Region.ClientStack.Linden } } - public void RegisterInventoryServiceHandlers(string capsBase) + public void RegisterInventoryServiceHandlers() { try { - // I don't think this one works... - m_HostCapsObj.RegisterHandler( - "NewFileAgentInventory", + m_HostCapsObj.RegisterHandler("NewFileAgentInventory", new LLSDStreamhandler( - "POST", - capsBase + m_newInventory, - NewAgentInventoryRequest, - "NewFileAgentInventory", - null)); - - IRequestHandler req - = new RestStreamHandler( - "POST", capsBase + m_notecardUpdatePath, NoteCardAgentInventory, "Update*", null); + "POST", GetNewCapPath(), NewAgentInventoryRequest, "NewFileAgentInventory", null)); + IRequestHandler req = new RestStreamHandler( + "POST", GetNewCapPath(), NoteCardAgentInventory, "Update*", null); m_HostCapsObj.RegisterHandler("UpdateNotecardAgentInventory", req); + m_HostCapsObj.RegisterHandler("UpdateAnimSetAgentInventory", req); m_HostCapsObj.RegisterHandler("UpdateScriptAgentInventory", req); m_HostCapsObj.RegisterHandler("UpdateScriptAgent", req); - IRequestHandler getObjectPhysicsDataHandler - = new RestStreamHandler( - "POST", capsBase + m_getObjectPhysicsDataPath, GetObjectPhysicsData, "GetObjectPhysicsData", null); - m_HostCapsObj.RegisterHandler("GetObjectPhysicsData", getObjectPhysicsDataHandler); - - IRequestHandler UpdateAgentInformationHandler - = new RestStreamHandler( - "POST", capsBase + m_UpdateAgentInformationPath, UpdateAgentInformation, "UpdateAgentInformation", null); + IRequestHandler UpdateAgentInformationHandler = new RestStreamHandler( + "POST", GetNewCapPath(), UpdateAgentInformation, "UpdateAgentInformation", null); m_HostCapsObj.RegisterHandler("UpdateAgentInformation", UpdateAgentInformationHandler); - m_HostCapsObj.RegisterHandler( - "CopyInventoryFromNotecard", - new RestStreamHandler( - "POST", capsBase + m_copyFromNotecardPath, CopyInventoryFromNotecard, "CopyInventoryFromNotecard", null)); - - // As of RC 1.22.9 of the Linden client this is - // supported - - //m_capsHandlers["WebFetchInventoryDescendents"] =new RestStreamHandler("POST", capsBase + m_fetchInventoryPath, FetchInventoryDescendentsRequest); - - // justincc: I've disabled the CAPS service for now to fix problems with selecting textures, and - // subsequent inventory breakage, in the edit object pane (such as mantis 1085). This requires - // enhancements (probably filling out the folder part of the LLSD reply) to our CAPS service, - // but when I went on the Linden grid, the - // simulators I visited (version 1.21) were, surprisingly, no longer supplying this capability. Instead, - // the 1.19.1.4 client appeared to be happily flowing inventory data over UDP - // - // This is very probably just a temporary measure - once the CAPS service appears again on the Linden grid - // we will be - // able to get the data we need to implement the necessary part of the protocol to fix the issue above. - // m_capsHandlers["FetchInventoryDescendents"] = - // new RestStreamHandler("POST", capsBase + m_fetchInventoryPath, FetchInventoryRequest); - - // m_capsHandlers["FetchInventoryDescendents"] = - // new LLSDStreamhandler("POST", - // capsBase + m_fetchInventory, - // FetchInventory)); - // m_capsHandlers["RequestTextureDownload"] = new RestStreamHandler("POST", - // capsBase + m_requestTexture, - // RequestTexture); + IRequestHandler CopyInventoryFromNotecardHandler = new RestStreamHandler( + "POST", GetNewCapPath(), CopyInventoryFromNotecard, "CopyInventoryFromNotecard", null); + m_HostCapsObj.RegisterHandler("CopyInventoryFromNotecard", CopyInventoryFromNotecardHandler); + } catch (Exception e) { @@ -256,6 +322,22 @@ namespace OpenSim.Region.ClientStack.Linden } } + public void RegisterOtherHandlers() + { + try + { + if (m_UserManager != null) + { + IRequestHandler GetDisplayNamesHandler = new RestStreamHandler( + "GET", GetNewCapPath(), GetDisplayNames, "GetDisplayNames", null); + m_HostCapsObj.RegisterHandler("GetDisplayNames", GetDisplayNamesHandler); + } + } + catch (Exception e) + { + m_log.Error("[CAPS]: " + e.ToString()); + } + } /// /// Construct a client response detailing all the capabilities this server can provide. /// @@ -268,8 +350,11 @@ namespace OpenSim.Region.ClientStack.Linden public string SeedCapRequest(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { -// m_log.DebugFormat( -// "[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID); + m_log.DebugFormat( + "[CAPS]: Received SEED caps request in {0} for agent {1}", m_regionName, m_HostCapsObj.AgentID); + + if (!m_HostCapsObj.WaitForActivation()) + return string.Empty; if (!m_Scene.CheckClient(m_HostCapsObj.AgentID, httpRequest.RemoteIPEndPoint)) { @@ -314,30 +399,28 @@ namespace OpenSim.Region.ClientStack.Linden LLSDTaskScriptUpdate llsdUpdateRequest = new LLSDTaskScriptUpdate(); LLSDHelpers.DeserialiseOSDMap(hash, llsdUpdateRequest); - string capsBase = "/CAPS/" + m_HostCapsObj.CapsObjectPath; - string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); + string uploaderPath = GetNewCapPath(); TaskInventoryScriptUpdater uploader = new TaskInventoryScriptUpdater( llsdUpdateRequest.item_id, llsdUpdateRequest.task_id, llsdUpdateRequest.is_script_running, - capsBase + uploaderPath, + uploaderPath, m_HostCapsObj.HttpListener, m_dumpAssetsToFile); uploader.OnUpLoad += TaskScriptUpdated; m_HostCapsObj.HttpListener.AddStreamHandler( new BinaryStreamHandler( - "POST", capsBase + uploaderPath, uploader.uploaderCaps, "TaskInventoryScriptUpdater", null)); + "POST", uploaderPath, uploader.uploaderCaps, "TaskInventoryScriptUpdater", null)); string protocol = "http://"; if (m_HostCapsObj.SSLCaps) protocol = "https://"; - string uploaderURL = protocol + m_HostCapsObj.HostName + ":" + m_HostCapsObj.Port.ToString() + capsBase + - uploaderPath; + string uploaderURL = protocol + m_HostCapsObj.HostName + ":" + m_HostCapsObj.Port.ToString() + uploaderPath; LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse(); uploadResponse.uploader = uploaderURL; @@ -400,83 +483,208 @@ namespace OpenSim.Region.ClientStack.Linden //m_log.Debug("[CAPS]: NewAgentInventoryRequest Request is: " + llsdRequest.ToString()); //m_log.Debug("asset upload request via CAPS" + llsdRequest.inventory_type + " , " + llsdRequest.asset_type); + // start by getting the client + IClientAPI client = null; + m_Scene.TryGetClient(m_HostCapsObj.AgentID, out client); + + // check current state so we only have one service at a time + lock (m_ModelCost) + { + switch (m_FileAgentInventoryState) + { + case FileAgentInventoryState.processRequest: + case FileAgentInventoryState.processUpload: + LLSDAssetUploadError resperror = new LLSDAssetUploadError(); + resperror.message = "Uploader busy processing previus request"; + resperror.identifier = UUID.Zero; + + LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse(); + errorResponse.uploader = ""; + errorResponse.state = "error"; + errorResponse.error = resperror; + return errorResponse; + break; + case FileAgentInventoryState.waitUpload: + // todo stop current uploader server + break; + case FileAgentInventoryState.idle: + default: + break; + } + + m_FileAgentInventoryState = FileAgentInventoryState.processRequest; + } + + int cost = 0; + int nreqtextures = 0; + int nreqmeshs= 0; + int nreqinstances = 0; + bool IsAtestUpload = false; + + string assetName = llsdRequest.name; + + LLSDAssetUploadResponseData meshcostdata = new LLSDAssetUploadResponseData(); + if (llsdRequest.asset_type == "texture" || llsdRequest.asset_type == "animation" || + llsdRequest.asset_type == "animatn" || // this is the asset name actually used by viewers + llsdRequest.asset_type == "mesh" || llsdRequest.asset_type == "sound") { ScenePresence avatar = null; - IClientAPI client = null; m_Scene.TryGetScenePresence(m_HostCapsObj.AgentID, out avatar); // check user level if (avatar != null) { - client = avatar.ControllingClient; - - if (avatar.UserLevel < m_levelUpload) + if (avatar.GodController.UserLevel < m_levelUpload) { - if (client != null) - client.SendAgentAlertMessage("Unable to upload asset. Insufficient permissions.", false); + LLSDAssetUploadError resperror = new LLSDAssetUploadError(); + resperror.message = "Insufficient permissions to upload"; + resperror.identifier = UUID.Zero; LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse(); errorResponse.uploader = ""; errorResponse.state = "error"; + errorResponse.error = resperror; + lock (m_ModelCost) + m_FileAgentInventoryState = FileAgentInventoryState.idle; return errorResponse; } } - // check funds + // check test upload and funds if (client != null) { IMoneyModule mm = m_Scene.RequestModuleInterface(); + int baseCost = 0; if (mm != null) + baseCost = mm.UploadCharge; + + string warning = String.Empty; + + if (llsdRequest.asset_type == "mesh") { - if (!mm.UploadCovered(client.AgentId, mm.UploadCharge)) + string error; + int modelcost; + + + if (!m_ModelCost.MeshModelCost(llsdRequest.asset_resources, baseCost, out modelcost, + meshcostdata, out error, ref warning)) { - client.SendAgentAlertMessage("Unable to upload asset. Insufficient funds.", false); + LLSDAssetUploadError resperror = new LLSDAssetUploadError(); + resperror.message = error; + resperror.identifier = UUID.Zero; LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse(); errorResponse.uploader = ""; errorResponse.state = "error"; + errorResponse.error = resperror; + + lock (m_ModelCost) + m_FileAgentInventoryState = FileAgentInventoryState.idle; return errorResponse; } + cost = modelcost; + } + else + { + cost = baseCost; } + + if (cost > 0 && mm != null) + { + // check for test upload + + if (m_ForceFreeTestUpload) // all are test + { + if (!(assetName.Length > 5 && assetName.StartsWith("TEST-"))) // has normal name lets change it + assetName = "TEST-" + assetName; + + IsAtestUpload = true; + } + + else if (m_enableFreeTestUpload) // only if prefixed with "TEST-" + { + + IsAtestUpload = (assetName.Length > 5 && assetName.StartsWith("TEST-")); + } + + + if(IsAtestUpload) // let user know, still showing cost estimation + warning += "Upload will have no cost, for testing purposes only. Other uses are prohibited. Items will not work after 48 hours or on other regions"; + + // check funds + else + { + if (!mm.UploadCovered(client.AgentId, (int)cost)) + { + LLSDAssetUploadError resperror = new LLSDAssetUploadError(); + resperror.message = "Insuficient funds"; + resperror.identifier = UUID.Zero; + + LLSDAssetUploadResponse errorResponse = new LLSDAssetUploadResponse(); + errorResponse.uploader = ""; + errorResponse.state = "error"; + errorResponse.error = resperror; + lock (m_ModelCost) + m_FileAgentInventoryState = FileAgentInventoryState.idle; + return errorResponse; + } + } + } + + if (client != null && warning != String.Empty) + client.SendAgentAlertMessage(warning, true); } } - string assetName = llsdRequest.name; string assetDes = llsdRequest.description; - string capsBase = "/CAPS/" + m_HostCapsObj.CapsObjectPath; UUID newAsset = UUID.Random(); UUID newInvItem = UUID.Random(); UUID parentFolder = llsdRequest.folder_id; - string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); + string uploaderPath = GetNewCapPath(); + UUID texturesFolder = UUID.Zero; + + if(!IsAtestUpload && m_enableModelUploadTextureToInventory) + texturesFolder = llsdRequest.texture_folder_id; AssetUploader uploader = new AssetUploader(assetName, assetDes, newAsset, newInvItem, parentFolder, llsdRequest.inventory_type, - llsdRequest.asset_type, capsBase + uploaderPath, m_HostCapsObj.HttpListener, m_dumpAssetsToFile); + llsdRequest.asset_type, uploaderPath, m_HostCapsObj.HttpListener, m_dumpAssetsToFile, cost, + texturesFolder, nreqtextures, nreqmeshs, nreqinstances, IsAtestUpload, + llsdRequest.next_owner_mask, llsdRequest.group_mask, llsdRequest.everyone_mask); m_HostCapsObj.HttpListener.AddStreamHandler( new BinaryStreamHandler( "POST", - capsBase + uploaderPath, + uploaderPath, uploader.uploaderCaps, "NewAgentInventoryRequest", m_HostCapsObj.AgentID.ToString())); string protocol = "http://"; - if (m_HostCapsObj.SSLCaps) protocol = "https://"; - string uploaderURL = protocol + m_HostCapsObj.HostName + ":" + m_HostCapsObj.Port.ToString() + capsBase + - uploaderPath; + string uploaderURL = protocol + m_HostCapsObj.HostName + ":" + m_HostCapsObj.Port.ToString() + uploaderPath; LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse(); uploadResponse.uploader = uploaderURL; uploadResponse.state = "upload"; + uploadResponse.upload_price = (int)cost; + + if (llsdRequest.asset_type == "mesh") + { + uploadResponse.data = meshcostdata; + } + uploader.OnUpLoad += UploadCompleteHandler; + + lock (m_ModelCost) + m_FileAgentInventoryState = FileAgentInventoryState.waitUpload; + return uploadResponse; } @@ -488,8 +696,14 @@ namespace OpenSim.Region.ClientStack.Linden /// public void UploadCompleteHandler(string assetName, string assetDescription, UUID assetID, UUID inventoryItem, UUID parentFolder, byte[] data, string inventoryType, - string assetType) + string assetType, int cost, + UUID texturesFolder, int nreqtextures, int nreqmeshs, int nreqinstances, + bool IsAtestUpload, ref string error, + ref int nextOwnerMask, ref int groupMask, ref int everyoneMask) { + lock (m_ModelCost) + m_FileAgentInventoryState = FileAgentInventoryState.processUpload; + m_log.DebugFormat( "[BUNCH OF CAPS]: Uploaded asset {0} for inventory item {1}, inv type {2}, asset type {3}", assetID, inventoryItem, inventoryType, assetType); @@ -497,6 +711,34 @@ namespace OpenSim.Region.ClientStack.Linden sbyte assType = 0; sbyte inType = 0; + IClientAPI client = null; + + UUID owner_id = m_HostCapsObj.AgentID; + UUID creatorID; + + bool istest = IsAtestUpload && m_enableFreeTestUpload && (cost > 0); + + bool restrictPerms = m_RestrictFreeTestUploadPerms && istest; + + if (istest && m_testAssetsCreatorID != UUID.Zero) + creatorID = m_testAssetsCreatorID; + else + creatorID = owner_id; + + string creatorIDstr = creatorID.ToString(); + + IMoneyModule mm = m_Scene.RequestModuleInterface(); + if (mm != null) + { + // make sure client still has enougth credit + if (!mm.UploadCovered(m_HostCapsObj.AgentID, (int)cost)) + { + error = "Insufficient funds."; + return; + } + } + + // strings to types if (inventoryType == "sound") { inType = (sbyte)InventoryType.Sound; @@ -511,6 +753,12 @@ namespace OpenSim.Region.ClientStack.Linden inType = (sbyte)InventoryType.Animation; assType = (sbyte)AssetType.Animation; } + else if (inventoryType == "animset") + { + inType = (sbyte)CustomInventoryType.AnimationSet; + assType = (sbyte)CustomAssetType.AnimationSet; + m_log.Debug("got animset upload request"); + } else if (inventoryType == "wearable") { inType = (sbyte)InventoryType.Wearable; @@ -526,191 +774,290 @@ namespace OpenSim.Region.ClientStack.Linden } else if (inventoryType == "object") { - inType = (sbyte)InventoryType.Object; - assType = (sbyte)AssetType.Object; - - List positions = new List(); - List rotations = new List(); - OSDMap request = (OSDMap)OSDParser.DeserializeLLSDXml(data); - OSDArray instance_list = (OSDArray)request["instance_list"]; - OSDArray mesh_list = (OSDArray)request["mesh_list"]; - OSDArray texture_list = (OSDArray)request["texture_list"]; - SceneObjectGroup grp = null; + if (assetType == "mesh") // this code for now is for mesh models uploads only + { + inType = (sbyte)InventoryType.Object; + assType = (sbyte)AssetType.Object; - InventoryFolderBase textureUploadFolder = null; + List positions = new List(); + List rotations = new List(); + OSDMap request = (OSDMap)OSDParser.DeserializeLLSDXml(data); - List foldersToUpdate = new List(); - List itemsToUpdate = new List(); - IClientInventory clientInv = null; - - if (texture_list.Count > 0) - { - ScenePresence avatar = null; - m_Scene.TryGetScenePresence(m_HostCapsObj.AgentID, out avatar); + // compare and get updated information +/* does nothing still we do need something to avoid special viewer to upload something diferent from the cost estimation + bool mismatchError = true; - if (avatar != null) + while (mismatchError) { - IClientCore core = (IClientCore)avatar.ControllingClient; + mismatchError = false; + } + + if (mismatchError) + { + error = "Upload and fee estimation information don't match"; + lock (m_ModelCost) + m_FileAgentInventoryState = FileAgentInventoryState.idle; + + return; + } +*/ + OSDArray instance_list = (OSDArray)request["instance_list"]; + OSDArray mesh_list = (OSDArray)request["mesh_list"]; + OSDArray texture_list = (OSDArray)request["texture_list"]; + SceneObjectGroup grp = null; + + // create and store texture assets + bool doTextInv = (!istest && m_enableModelUploadTextureToInventory && + texturesFolder != UUID.Zero); + + + List textures = new List(); + + +// if (doTextInv) + m_Scene.TryGetClient(m_HostCapsObj.AgentID, out client); + + if(client == null) // don't put textures in inventory if there is no client + doTextInv = false; - if (core.TryGet(out clientInv)) + for (int i = 0; i < texture_list.Count; i++) + { + AssetBase textureAsset = new AssetBase(UUID.Random(), assetName, (sbyte)AssetType.Texture, creatorIDstr); + textureAsset.Data = texture_list[i].AsBinary(); + if (istest) + textureAsset.Local = true; + m_assetService.Store(textureAsset); + textures.Add(textureAsset.FullID); + + if (doTextInv) { - var systemTextureFolder = m_Scene.InventoryService.GetFolderForType(m_HostCapsObj.AgentID, FolderType.Texture); - textureUploadFolder = new InventoryFolderBase(UUID.Random(), assetName, m_HostCapsObj.AgentID, (short)FolderType.None, systemTextureFolder.ID, 1); - if (m_Scene.InventoryService.AddFolder(textureUploadFolder)) - { - foldersToUpdate.Add(textureUploadFolder); + string name = assetName; + if (name.Length > 25) + name = name.Substring(0, 24); + name += "_Texture#" + i.ToString(); + InventoryItemBase texitem = new InventoryItemBase(); + texitem.Owner = m_HostCapsObj.AgentID; + texitem.CreatorId = creatorIDstr; + texitem.CreatorData = String.Empty; + texitem.ID = UUID.Random(); + texitem.AssetID = textureAsset.FullID; + texitem.Description = "mesh model texture"; + texitem.Name = name; + texitem.AssetType = (int)AssetType.Texture; + texitem.InvType = (int)InventoryType.Texture; + texitem.Folder = texturesFolder; + + texitem.CurrentPermissions + = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer | PermissionMask.Export); + + texitem.BasePermissions = (uint)PermissionMask.All | (uint)PermissionMask.Export; + texitem.EveryOnePermissions = 0; + texitem.NextPermissions = (uint)PermissionMask.All; + texitem.CreationDate = Util.UnixTimeSinceEpoch(); + + m_Scene.AddInventoryItem(client, texitem); + texitem = null; + } + } - m_log.DebugFormat( - "[BUNCH OF CAPS]: Created new folder '{0}' ({1}) for textures uploaded with mesh object {2}", - textureUploadFolder.Name, textureUploadFolder.ID, assetName); - } - else + // create and store meshs assets + List meshAssets = new List(); + List meshAvatarSkeletons = new List(); + List meshAvatarColliders = new List(); + + bool curAvSkeleton; + bool curAvCollider; + for (int i = 0; i < mesh_list.Count; i++) + { + curAvSkeleton = false; + curAvCollider = false; + + // we do need to parse the mesh now + OSD osd = OSDParser.DeserializeLLSDBinary(mesh_list[i]); + if (osd is OSDMap) + { + OSDMap mosd = (OSDMap)osd; + if (mosd.ContainsKey("skeleton")) { - textureUploadFolder = null; + OSDMap skeleton = (OSDMap)mosd["skeleton"]; + int sksize = skeleton["size"].AsInteger(); + if (sksize > 0) + curAvSkeleton = true; } } - } - } - List textures = new List(); - for (int i = 0; i < texture_list.Count; i++) - { - AssetBase textureAsset = new AssetBase(UUID.Random(), assetName, (sbyte)AssetType.Texture, ""); - textureAsset.Data = texture_list[i].AsBinary(); - m_assetService.Store(textureAsset); - textures.Add(textureAsset.FullID); + AssetBase meshAsset = new AssetBase(UUID.Random(), assetName, (sbyte)AssetType.Mesh, creatorIDstr); + meshAsset.Data = mesh_list[i].AsBinary(); + if (istest) + meshAsset.Local = true; + m_assetService.Store(meshAsset); + meshAssets.Add(meshAsset.FullID); + meshAvatarSkeletons.Add(curAvSkeleton); + meshAvatarColliders.Add(curAvCollider); + + // test code + if (curAvSkeleton && client != null) + { + string name = assetName; + if (name.Length > 25) + name = name.Substring(0, 24); + name += "_Mesh#" + i.ToString(); + InventoryItemBase meshitem = new InventoryItemBase(); + meshitem.Owner = m_HostCapsObj.AgentID; + meshitem.CreatorId = creatorIDstr; + meshitem.CreatorData = String.Empty; + meshitem.ID = UUID.Random(); + meshitem.AssetID = meshAsset.FullID; + meshitem.Description = "mesh "; + meshitem.Name = name; + meshitem.AssetType = (int)AssetType.Mesh; + meshitem.InvType = (int)InventoryType.Mesh; + // meshitem.Folder = UUID.Zero; // send to default + + meshitem.Folder = parentFolder; // dont let it go to folder Meshes that viewers dont show + + // If we set PermissionMask.All then when we rez the item the next permissions will replace the current + // (owner) permissions. This becomes a problem if next permissions are changed. + meshitem.CurrentPermissions + = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer); + + meshitem.BasePermissions = (uint)PermissionMask.All; + meshitem.EveryOnePermissions = 0; + meshitem.NextPermissions = (uint)PermissionMask.All; + meshitem.CreationDate = Util.UnixTimeSinceEpoch(); + + m_Scene.AddInventoryItem(client, meshitem); + meshitem = null; + } + } - if (textureUploadFolder != null) + int skipedMeshs = 0; + // build prims from instances + for (int i = 0; i < instance_list.Count; i++) { - InventoryItemBase textureItem = new InventoryItemBase(); - textureItem.Owner = m_HostCapsObj.AgentID; - textureItem.CreatorId = m_HostCapsObj.AgentID.ToString(); - textureItem.CreatorData = String.Empty; - textureItem.ID = UUID.Random(); - textureItem.AssetID = textureAsset.FullID; - textureItem.Description = assetDescription; - textureItem.Name = assetName + " - Texture " + (i + 1).ToString(); - textureItem.AssetType = (int)AssetType.Texture; - textureItem.InvType = (int)InventoryType.Texture; - textureItem.Folder = textureUploadFolder.ID; - textureItem.CurrentPermissions - = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer | PermissionMask.Export); - textureItem.BasePermissions = (uint)PermissionMask.All | (uint)PermissionMask.Export; - textureItem.EveryOnePermissions = 0; - textureItem.NextPermissions = (uint)PermissionMask.All; - textureItem.CreationDate = Util.UnixTimeSinceEpoch(); - m_Scene.InventoryService.AddItem(textureItem); - itemsToUpdate.Add(textureItem); - - m_log.DebugFormat( - "[BUNCH OF CAPS]: Created new inventory item '{0}' ({1}) for texture uploaded with mesh object {2}", - textureItem.Name, textureItem.ID, assetName); - } - } + OSDMap inner_instance_list = (OSDMap)instance_list[i]; - if (clientInv != null && (foldersToUpdate.Count > 0 || itemsToUpdate.Count > 0)) - { - clientInv.SendBulkUpdateInventory(foldersToUpdate.ToArray(), itemsToUpdate.ToArray()); - } + // skip prims that are 2 small + Vector3 scale = inner_instance_list["scale"].AsVector3(); - for (int i = 0; i < mesh_list.Count; i++) - { - PrimitiveBaseShape pbs = PrimitiveBaseShape.CreateBox(); + if (scale.X < m_PrimScaleMin || scale.Y < m_PrimScaleMin || scale.Z < m_PrimScaleMin) + { + skipedMeshs++; + continue; + } - Primitive.TextureEntry textureEntry - = new Primitive.TextureEntry(Primitive.TextureEntry.WHITE_TEXTURE); - OSDMap inner_instance_list = (OSDMap)instance_list[i]; + OSDArray face_list = (OSDArray)inner_instance_list["face_list"]; - OSDArray face_list = (OSDArray)inner_instance_list["face_list"]; - for (uint face = 0; face < face_list.Count; face++) - { - OSDMap faceMap = (OSDMap)face_list[(int)face]; - Primitive.TextureEntryFace f = pbs.Textures.CreateFace(face); - if(faceMap.ContainsKey("fullbright")) - f.Fullbright = faceMap["fullbright"].AsBoolean(); - if (faceMap.ContainsKey ("diffuse_color")) - f.RGBA = faceMap["diffuse_color"].AsColor4(); + PrimitiveBaseShape pbs = null; + if (inner_instance_list.ContainsKey("mesh")) // seems to happen always but ... + { + int meshindx = inner_instance_list["mesh"].AsInteger(); + if (meshAssets.Count > meshindx) + pbs = PrimitiveBaseShape.CreateMesh(face_list.Count, meshAssets[meshindx]); + } + if(pbs == null) // fallback + pbs = PrimitiveBaseShape.CreateBox(); + + Primitive.TextureEntry textureEntry + = new Primitive.TextureEntry(Primitive.TextureEntry.WHITE_TEXTURE); - int textureNum = faceMap["image"].AsInteger(); - float imagerot = faceMap["imagerot"].AsInteger(); - float offsets = (float)faceMap["offsets"].AsReal(); - float offsett = (float)faceMap["offsett"].AsReal(); - float scales = (float)faceMap["scales"].AsReal(); - float scalet = (float)faceMap["scalet"].AsReal(); + for (uint face = 0; face < face_list.Count; face++) + { + OSDMap faceMap = (OSDMap)face_list[(int)face]; - if(imagerot != 0) - f.Rotation = imagerot; + Primitive.TextureEntryFace f = textureEntry.CreateFace(face); //clone the default + if (faceMap.ContainsKey("fullbright")) + f.Fullbright = faceMap["fullbright"].AsBoolean(); + if (faceMap.ContainsKey("diffuse_color")) + f.RGBA = faceMap["diffuse_color"].AsColor4(); - if(offsets != 0) - f.OffsetU = offsets; + int textureNum = faceMap["image"].AsInteger(); + float imagerot = faceMap["imagerot"].AsInteger(); + float offsets = (float)faceMap["offsets"].AsReal(); + float offsett = (float)faceMap["offsett"].AsReal(); + float scales = (float)faceMap["scales"].AsReal(); + float scalet = (float)faceMap["scalet"].AsReal(); - if (offsett != 0) - f.OffsetV = offsett; + if (imagerot != 0) + f.Rotation = imagerot; - if (scales != 0) - f.RepeatU = scales; + if (offsets != 0) + f.OffsetU = offsets; - if (scalet != 0) - f.RepeatV = scalet; + if (offsett != 0) + f.OffsetV = offsett; - if (textures.Count > textureNum) - f.TextureID = textures[textureNum]; + if (scales != 0) + f.RepeatU = scales; + + if (scalet != 0) + f.RepeatV = scalet; + + if (textures.Count > textureNum) + f.TextureID = textures[textureNum]; + + textureEntry.FaceTextures[face] = f; + } + pbs.TextureEntry = textureEntry.GetBytes(); + + Vector3 position = inner_instance_list["position"].AsVector3(); + Quaternion rotation = inner_instance_list["rotation"].AsQuaternion(); + + // for now viwers do send fixed defaults + // but this may change +// int physicsShapeType = inner_instance_list["physics_shape_type"].AsInteger(); + byte physicsShapeType = (byte)PhysShapeType.convex; // default is simple convex +// int material = inner_instance_list["material"].AsInteger(); + byte material = (byte)Material.Wood; + + SceneObjectPart prim + = new SceneObjectPart(owner_id, pbs, position, Quaternion.Identity, Vector3.Zero); + + prim.Scale = scale; + rotations.Add(rotation); + positions.Add(position); + prim.UUID = UUID.Random(); + prim.CreatorID = creatorID; + prim.OwnerID = owner_id; + prim.GroupID = UUID.Zero; + prim.LastOwnerID = creatorID; + prim.RezzerID = creatorID; + prim.CreationDate = Util.UnixTimeSinceEpoch(); + + if (grp == null) + prim.Name = assetName; else - f.TextureID = Primitive.TextureEntry.WHITE_TEXTURE; + prim.Name = assetName + "#" + i.ToString(); - textureEntry.FaceTextures[face] = f; - } + prim.EveryoneMask = 0; + prim.GroupMask = 0; - pbs.TextureEntry = textureEntry.GetBytes(); - - AssetBase meshAsset = new AssetBase(UUID.Random(), assetName, (sbyte)AssetType.Mesh, ""); - meshAsset.Data = mesh_list[i].AsBinary(); - m_assetService.Store(meshAsset); - - pbs.SculptEntry = true; - pbs.SculptTexture = meshAsset.FullID; - pbs.SculptType = (byte)SculptType.Mesh; - pbs.SculptData = meshAsset.Data; - - Vector3 position = inner_instance_list["position"].AsVector3(); - Vector3 scale = inner_instance_list["scale"].AsVector3(); - Quaternion rotation = inner_instance_list["rotation"].AsQuaternion(); - -// no longer used - begin ------------------------ -// int physicsShapeType = inner_instance_list["physics_shape_type"].AsInteger(); -// int material = inner_instance_list["material"].AsInteger(); -// int mesh = inner_instance_list["mesh"].AsInteger(); - -// OSDMap permissions = (OSDMap)inner_instance_list["permissions"]; -// int base_mask = permissions["base_mask"].AsInteger(); -// int everyone_mask = permissions["everyone_mask"].AsInteger(); -// UUID creator_id = permissions["creator_id"].AsUUID(); -// UUID group_id = permissions["group_id"].AsUUID(); -// int group_mask = permissions["group_mask"].AsInteger(); -// bool is_owner_group = permissions["is_owner_group"].AsBoolean(); -// UUID last_owner_id = permissions["last_owner_id"].AsUUID(); -// int next_owner_mask = permissions["next_owner_mask"].AsInteger(); -// UUID owner_id = permissions["owner_id"].AsUUID(); -// int owner_mask = permissions["owner_mask"].AsInteger(); -// no longer used - end ------------------------ - - UUID owner_id = m_HostCapsObj.AgentID; - - SceneObjectPart prim - = new SceneObjectPart(owner_id, pbs, position, Quaternion.Identity, Vector3.Zero); - - prim.Scale = scale; - //prim.OffsetPosition = position; - rotations.Add(rotation); - positions.Add(position); - prim.UUID = UUID.Random(); - prim.CreatorID = owner_id; - prim.OwnerID = owner_id; - prim.GroupID = UUID.Zero; - prim.LastOwnerID = prim.OwnerID; - prim.CreationDate = Util.UnixTimeSinceEpoch(); - prim.Name = assetName; - prim.Description = ""; + if (restrictPerms) + { + prim.BaseMask = (uint)(PermissionMask.Move | PermissionMask.Modify); + prim.OwnerMask = (uint)(PermissionMask.Move | PermissionMask.Modify); + prim.NextOwnerMask = 0; + } + else + { + prim.BaseMask = (uint)PermissionMask.All | (uint)PermissionMask.Export; + prim.OwnerMask = (uint)PermissionMask.All | (uint)PermissionMask.Export; + prim.GroupMask = prim.BaseMask & (uint)groupMask; + prim.EveryoneMask = prim.BaseMask & (uint)everyoneMask; + prim.NextOwnerMask = prim.BaseMask & (uint)nextOwnerMask; + // If the viewer gives us bogus permissions, revert to the SL + // default of transfer only. + if ((prim.NextOwnerMask & (uint)PermissionMask.All) == 0) + prim.NextOwnerMask = (uint)PermissionMask.Transfer; + } + + if(istest) + prim.Description = "For testing only. Other uses are prohibited"; + else + prim.Description = ""; + + prim.Material = material; + prim.PhysicsShapeType = physicsShapeType; // prim.BaseMask = (uint)base_mask; // prim.EveryoneMask = (uint)everyone_mask; @@ -718,52 +1065,65 @@ namespace OpenSim.Region.ClientStack.Linden // prim.NextOwnerMask = (uint)next_owner_mask; // prim.OwnerMask = (uint)owner_mask; - if (grp == null) - grp = new SceneObjectGroup(prim); - else - grp.AddPart(prim); - } + if (grp == null) + { + grp = new SceneObjectGroup(prim); + grp.LastOwnerID = creatorID; + grp.RezzerID = creatorID; + } + else + grp.AddPart(prim); + } - Vector3 rootPos = positions[0]; + Vector3 rootPos = positions[0]; - if (grp.Parts.Length > 1) - { - // Fix first link number - grp.RootPart.LinkNum++; + if (grp.Parts.Length > 1) + { + // Fix first link number + grp.RootPart.LinkNum++; - Quaternion rootRotConj = Quaternion.Conjugate(rotations[0]); - Quaternion tmprot; - Vector3 offset; + Quaternion rootRotConj = Quaternion.Conjugate(rotations[0]); + Quaternion tmprot; + Vector3 offset; - // fix children rotations and positions - for (int i = 1; i < rotations.Count; i++) - { - tmprot = rotations[i]; - tmprot = rootRotConj * tmprot; + // fix children rotations and positions + for (int i = 1; i < rotations.Count; i++) + { + tmprot = rotations[i]; + tmprot = rootRotConj * tmprot; - grp.Parts[i].RotationOffset = tmprot; + grp.Parts[i].RotationOffset = tmprot; - offset = positions[i] - rootPos; + offset = positions[i] - rootPos; + + offset *= rootRotConj; + grp.Parts[i].OffsetPosition = offset; + } - offset *= rootRotConj; - grp.Parts[i].OffsetPosition = offset; + grp.AbsolutePosition = rootPos; + grp.UpdateGroupRotationR(rotations[0]); + } + else + { + grp.AbsolutePosition = rootPos; + grp.UpdateGroupRotationR(rotations[0]); } - grp.AbsolutePosition = rootPos; - grp.UpdateGroupRotationR(rotations[0]); + data = ASCIIEncoding.ASCII.GetBytes(SceneObjectSerializer.ToOriginalXmlFormat(grp)); } - else + + else // not a mesh model { - grp.AbsolutePosition = rootPos; - grp.UpdateGroupRotationR(rotations[0]); + m_log.ErrorFormat("[CAPS Asset Upload] got unsuported assetType for object upload"); + return; } - - data = ASCIIEncoding.ASCII.GetBytes(SceneObjectSerializer.ToOriginalXmlFormat(grp)); } AssetBase asset; - asset = new AssetBase(assetID, assetName, assType, m_HostCapsObj.AgentID.ToString()); + asset = new AssetBase(assetID, assetName, assType, creatorIDstr); asset.Data = data; + if (istest) + asset.Local = true; if (AddNewAsset != null) AddNewAsset(asset); else if (m_assetService != null) @@ -771,11 +1131,17 @@ namespace OpenSim.Region.ClientStack.Linden InventoryItemBase item = new InventoryItemBase(); item.Owner = m_HostCapsObj.AgentID; - item.CreatorId = m_HostCapsObj.AgentID.ToString(); + item.CreatorId = creatorIDstr; item.CreatorData = String.Empty; item.ID = inventoryItem; item.AssetID = asset.FullID; - item.Description = assetDescription; + if (istest) + { + item.Description = "For testing only. Other uses are prohibited"; + item.Flags = (uint) (InventoryItemFlags.SharedSingleReference); + } + else + item.Description = assetDescription; item.Name = assetName; item.AssetType = assType; item.InvType = inType; @@ -783,18 +1149,69 @@ namespace OpenSim.Region.ClientStack.Linden // If we set PermissionMask.All then when we rez the item the next permissions will replace the current // (owner) permissions. This becomes a problem if next permissions are changed. - item.CurrentPermissions - = (uint)(PermissionMask.Move | PermissionMask.Copy | PermissionMask.Modify | PermissionMask.Transfer | PermissionMask.Export); - item.BasePermissions = (uint)PermissionMask.All | (uint)PermissionMask.Export; - item.EveryOnePermissions = 0; - item.NextPermissions = (uint)PermissionMask.All; + if (inType == (sbyte)CustomInventoryType.AnimationSet) + { + AnimationSet.setCreateItemPermitions(item); + } + + else if (restrictPerms) + { + item.BasePermissions = (uint)(PermissionMask.Move | PermissionMask.Modify); + item.CurrentPermissions = (uint)(PermissionMask.Move | PermissionMask.Modify); + item.GroupPermissions = 0; + item.EveryOnePermissions = 0; + item.NextPermissions = 0; + } + else + { + item.BasePermissions = (uint)PermissionMask.All | (uint)PermissionMask.Export; + item.CurrentPermissions = (uint)PermissionMask.All | (uint)PermissionMask.Export; + item.GroupPermissions = item.BasePermissions & (uint)groupMask; + item.EveryOnePermissions = item.BasePermissions & (uint)everyoneMask; + item.NextPermissions = item.BasePermissions & (uint)nextOwnerMask; + if ((item.NextPermissions & (uint)PermissionMask.All) == 0) + item.NextPermissions = (uint)PermissionMask.Transfer; + } + item.CreationDate = Util.UnixTimeSinceEpoch(); + everyoneMask = (int)item.EveryOnePermissions; + groupMask = (int)item.GroupPermissions; + nextOwnerMask = (int)item.NextPermissions; + + m_Scene.TryGetClient(m_HostCapsObj.AgentID, out client); + if (AddNewInventoryItem != null) { - AddNewInventoryItem(m_HostCapsObj.AgentID, item); + if (istest) + { + m_Scene.AddInventoryItem(client, item); +/* + AddNewInventoryItem(m_HostCapsObj.AgentID, item, 0); + if (client != null) + client.SendAgentAlertMessage("Upload will have no cost, for personal test purposes only. Other uses are forbiden. Items may not work on a another region" , true); + */ + } + else + { + AddNewInventoryItem(m_HostCapsObj.AgentID, item, (uint)cost); +// if (client != null) +// { +// // let users see anything.. i don't so far +// string str; +// if (cost > 0) +// // dont remember where is money unit name to put here +// str = "Upload complete. charged " + cost.ToString() + "$"; +// else +// str = "Upload complete"; +// client.SendAgentAlertMessage(str, true); +// } + } } + + lock (m_ModelCost) + m_FileAgentInventoryState = FileAgentInventoryState.idle; } /// @@ -857,24 +1274,22 @@ namespace OpenSim.Region.ClientStack.Linden LLSDItemUpdate llsdRequest = new LLSDItemUpdate(); LLSDHelpers.DeserialiseOSDMap(hash, llsdRequest); - string capsBase = "/CAPS/" + m_HostCapsObj.CapsObjectPath; - string uploaderPath = Util.RandomClass.Next(5000, 8000).ToString("0000"); + string uploaderPath = GetNewCapPath(); ItemUpdater uploader = - new ItemUpdater(llsdRequest.item_id, capsBase + uploaderPath, m_HostCapsObj.HttpListener, m_dumpAssetsToFile); + new ItemUpdater(llsdRequest.item_id, uploaderPath, m_HostCapsObj.HttpListener, m_dumpAssetsToFile); uploader.OnUpLoad += ItemUpdated; m_HostCapsObj.HttpListener.AddStreamHandler( new BinaryStreamHandler( - "POST", capsBase + uploaderPath, uploader.uploaderCaps, "NoteCardAgentInventory", null)); + "POST", uploaderPath, uploader.uploaderCaps, "NoteCardAgentInventory", null)); string protocol = "http://"; if (m_HostCapsObj.SSLCaps) protocol = "https://"; - string uploaderURL = protocol + m_HostCapsObj.HostName + ":" + m_HostCapsObj.Port.ToString() + capsBase + - uploaderPath; + string uploaderURL = protocol + m_HostCapsObj.HostName + ":" + m_HostCapsObj.Port.ToString() + uploaderPath; LLSDAssetUploadResponse uploadResponse = new LLSDAssetUploadResponse(); uploadResponse.uploader = uploaderURL; @@ -930,29 +1345,22 @@ namespace OpenSim.Region.ClientStack.Linden IClientAPI client = null; m_Scene.TryGetClient(m_HostCapsObj.AgentID, out client); - item = m_Scene.InventoryService.GetItem(new InventoryItemBase(itemID)); + item = m_Scene.InventoryService.GetItem(m_HostCapsObj.AgentID, itemID); if (item != null) { string message; copyItem = m_Scene.GiveInventoryItem(m_HostCapsObj.AgentID, item.Owner, itemID, folderID, out message); - if (client != null) + if (copyItem != null && client != null) { - if (copyItem != null) - { - m_log.InfoFormat("[CAPS]: CopyInventoryFromNotecard, ItemID:{0}, FolderID:{1}", copyItem.ID, copyItem.Folder); - client.SendBulkUpdateInventory(copyItem); - } - else - { - client.SendAgentAlertMessage(message, false); - } + m_log.InfoFormat("[CAPS]: CopyInventoryFromNotecard, ItemID:{0}, FolderID:{1}", copyItem.ID, copyItem.Folder); + client.SendBulkUpdateInventory(copyItem); } } else { m_log.ErrorFormat("[CAPS]: CopyInventoryFromNotecard - Failed to retrieve item {0} from notecard {1}", itemID, notecardID); if (client != null) - client.SendAgentAlertMessage("Failed to retrieve item", false); + client.SendAlertMessage("Failed to retrieve item"); } } catch (Exception e) @@ -995,26 +1403,483 @@ namespace OpenSim.Region.ClientStack.Linden return response; } - public string UpdateAgentInformation(string request, string path, + public string GetObjectCost(string request, string path, string param, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) { OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); - OSDMap accessPrefs = (OSDMap)req["access_prefs"]; - string desiredMaturity = accessPrefs["max"]; + OSDMap resp = new OSDMap(); + + OSDArray object_ids = (OSDArray)req["object_ids"]; + + for (int i = 0; i < object_ids.Count; i++) + { + UUID uuid = object_ids[i].AsUUID(); + + SceneObjectPart part = m_Scene.GetSceneObjectPart(uuid); + SceneObjectGroup grp = null; + if (part != null) + grp = part.ParentGroup; + if (grp != null) + { + float linksetCost; + float linksetPhysCost; + float partCost; + float partPhysCost; + + grp.GetResourcesCosts(part,out linksetCost,out linksetPhysCost,out partCost,out partPhysCost); + + OSDMap object_data = new OSDMap(); + object_data["linked_set_resource_cost"] = linksetCost; + object_data["resource_cost"] = partCost; + object_data["physics_cost"] = partPhysCost; + object_data["linked_set_physics_cost"] = linksetPhysCost; + object_data["resource_limiting_type"] = "legacy"; + resp[uuid.ToString()] = object_data; + } + } + if(resp.Count == 0) + { + OSDMap object_data = new OSDMap(); + object_data["linked_set_resource_cost"] = 0; + object_data["resource_cost"] = 0; + object_data["physics_cost"] = 0; + object_data["linked_set_physics_cost"] = 0; + resp[UUID.Zero.ToString()] = object_data; + } + string response = OSDParser.SerializeLLSDXmlString(resp); + return response; + } + + public string ResourceCostSelected(string request, string path, + string param, IOSHttpRequest httpRequest, + IOSHttpResponse httpResponse) + { + OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); + OSDMap resp = new OSDMap(); + + + float phys=0; + float stream=0; + float simul=0; + + if (req.ContainsKey("selected_roots")) + { + OSDArray object_ids = (OSDArray)req["selected_roots"]; + + // should go by SOG suming costs for all parts + // ll v3 works ok with several objects select we get the list and adds ok + // FS calls per object so results are wrong guess fs bug + for (int i = 0; i < object_ids.Count; i++) + { + UUID uuid = object_ids[i].AsUUID(); + float Physc; + float simulc; + float streamc; + + SceneObjectGroup grp = m_Scene.GetGroupByPrim(uuid); + if (grp != null) + { + grp.GetSelectedCosts(out Physc, out streamc, out simulc); + phys += Physc; + stream += streamc; + simul += simulc; + } + } + } + else if (req.ContainsKey("selected_prims")) + { + OSDArray object_ids = (OSDArray)req["selected_prims"]; + + // don't see in use in any of the 2 viewers + // guess it should be for edit linked but... nothing + // should go to SOP per part + for (int i = 0; i < object_ids.Count; i++) + { + UUID uuid = object_ids[i].AsUUID(); + + SceneObjectPart part = m_Scene.GetSceneObjectPart(uuid); + if (part != null) + { + phys += part.PhysicsCost; + stream += part.StreamingCost; + simul += part.SimulationCost; + } + } + } + + OSDMap object_data = new OSDMap(); + + object_data["physics"] = phys; + object_data["streaming"] = stream; + object_data["simulation"] = simul; + + resp["selected"] = object_data; +// resp["transaction_id"] = "undef"; + string response = OSDParser.SerializeLLSDXmlString(resp); + return response; + } + public string UpdateAgentInformation(string request, string path, + string param, IOSHttpRequest httpRequest, + IOSHttpResponse httpResponse) + { +// OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); OSDMap resp = new OSDMap(); - OSDMap respAccessPrefs = new OSDMap(); - respAccessPrefs["max"] = desiredMaturity; // echoing the maturity back means success - resp["access_prefs"] = respAccessPrefs; + + OSDMap accessPrefs = new OSDMap(); + accessPrefs["max"] = "A"; + + resp["access_prefs"] = accessPrefs; string response = OSDParser.SerializeLLSDXmlString(resp); - return response; + return response; + } + + public bool OSDMapTOVector3(OSDMap map, out Vector3 v) + { + v = Vector3.Zero; + if(!map.ContainsKey("X")) + return false; + if(!map.ContainsKey("Y")) + return false; + if(!map.ContainsKey("Z")) + return false; + v.X = (float)map["X"].AsReal(); + v.Y = (float)map["Y"].AsReal(); + v.Z = (float)map["Z"].AsReal(); + return true; + } + + public string HomeLocation(string request, string path, string param, IOSHttpRequest httpRequest, + IOSHttpResponse httpResponse) + { + OSDMap resp = new OSDMap(); + + resp["success"] = "false"; + + + bool fail = true; + string message = "Set Home request failed"; + int locationID = 1; + Vector3 pos = Vector3.Zero; + Vector3 lookAt = Vector3.Zero; + + IClientAPI client = null; + ScenePresence sp; + + while(true) + { + if(m_Scene.GridUserService == null) + break; + + if(m_Scene.UserManagementModule == null) + break; + + m_Scene.TryGetScenePresence(m_AgentID, out sp); + if(sp == null || sp.IsChildAgent || sp.IsDeleted) + break; + + if(sp.IsInTransit && !sp.IsInLocalTransit) + break; + + client = sp.ControllingClient; + + if(!m_Scene.UserManagementModule.IsLocalGridUser(m_AgentID)) + break; + + OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); + if(!req.ContainsKey("HomeLocation")) + break; + + OSDMap HLocation = (OSDMap)req["HomeLocation"]; + if(!HLocation.ContainsKey("LocationPos")) + break; + if(!HLocation.ContainsKey("LocationLookAt")) + break; + + locationID = HLocation["LocationId"].AsInteger(); + + if(!OSDMapTOVector3((OSDMap)HLocation["LocationPos"], out pos)) + break; + + if(!OSDMapTOVector3((OSDMap)HLocation["LocationLookAt"], out lookAt)) + break; + + ILandObject land = m_Scene.LandChannel.GetLandObject(pos); + if(land == null) + break; + + ulong gpowers = client.GetGroupPowers(land.LandData.GroupID); + SceneObjectGroup telehub = null; + if (m_Scene.RegionInfo.RegionSettings.TelehubObject != UUID.Zero) + // Does the telehub exist in the scene? + telehub = m_Scene.GetSceneObjectGroup(m_Scene.RegionInfo.RegionSettings.TelehubObject); + + if (!m_Scene.Permissions.IsAdministrator(m_AgentID) && // (a) gods and land managers can set home + !m_Scene.Permissions.IsGod(m_AgentID) && + m_AgentID != land.LandData.OwnerID && // (b) land owners can set home + // (c) members of the land-associated group in roles that can set home + ((gpowers & (ulong)GroupPowers.AllowSetHome) != (ulong)GroupPowers.AllowSetHome) && + // (d) parcels with telehubs can be the home of anyone + (telehub == null || !land.ContainsPoint((int)telehub.AbsolutePosition.X, (int)telehub.AbsolutePosition.Y))) + { + message = "You are not allowed to set your home location in this parcel."; + break; + } + + string userId; + UUID test; + if (!m_Scene.UserManagementModule.GetUserUUI(m_AgentID, out userId)) + { + message = "Set Home request failed. (User Lookup)"; + break; + } + + if (!UUID.TryParse(userId, out test)) + { + message = "Set Home request failed. (HG visitor)"; + break; + } + + if (m_Scene.GridUserService.SetHome(userId, land.RegionUUID, pos, lookAt)) + fail = false; + + break; + } + + string response; + + if(fail) + { + if(client != null) + client.SendAlertMessage(message); + response = OSDParser.SerializeLLSDXmlString(resp); + return response; + } + + // so its http but still needs a udp reply to inform user? crap :p + if(client != null) + client.SendAlertMessage("Home position set.","HomePositionSet"); + + resp["success"] = "true"; + OSDMap homeloc = new OSDMap(); + OSDMap homelocpos = new OSDMap(); + // for some odd reason viewers send pos as reals but read as integer + homelocpos["X"] = new OSDReal(pos.X); + homelocpos["Y"] = new OSDReal(pos.Y); + homelocpos["Z"] = new OSDReal(pos.Z); + homeloc["LocationPos"] = homelocpos; + + resp["HomeLocation"] = homeloc; + + response = OSDParser.SerializeLLSDXmlString(resp); + return response; + } + + private static int CompareRolesByMembersDesc(GroupRolesData x, GroupRolesData y) + { + return -(x.Members.CompareTo(y.Members)); + } + + public string GroupMemberData(string request, string path, string param, IOSHttpRequest httpRequest, + IOSHttpResponse httpResponse) + { + OSDMap resp = new OSDMap(); + + string response; + + bool fail = true; + IClientAPI client = null; + ScenePresence sp; + IGroupsModule m_GroupsModule; + UUID groupID = UUID.Zero; + + while(true) + { + m_GroupsModule = m_Scene.RequestModuleInterface(); + if(m_GroupsModule == null) + break; + + m_Scene.TryGetScenePresence(m_AgentID, out sp); + if(sp == null || sp.IsChildAgent || sp.IsDeleted) + break; + + if(sp.IsInTransit && !sp.IsInLocalTransit) + break; + + client = sp.ControllingClient; + + OSDMap req = (OSDMap)OSDParser.DeserializeLLSDXml(request); + if(!req.ContainsKey("group_id")) + break; + + groupID = req["group_id"].AsUUID(); + if(groupID == UUID.Zero) + break; + + List roles = m_GroupsModule.GroupRoleDataRequest(client, groupID); + if(roles == null || roles.Count == 0) + break; + + List members = m_GroupsModule.GroupMembersRequest(client, groupID); + if(members == null || members.Count == 0) + break; + + int memberCount = members.Count; + + Dictionary titles = new Dictionary(); + int i = 0; + + ulong defaultPowers = 0; + + + // build titles array and index + roles.Sort(CompareRolesByMembersDesc); + + OSDArray osdtitles = new OSDArray(); + foreach(GroupRolesData grd in roles) + { + if(grd.Title == null) + continue; + string title = grd.Title; + if(i==0) + defaultPowers = grd.Powers; + + if(!titles.ContainsKey(title)) + { + titles[title] = i++; + osdtitles.Add(new OSDString(title)); + } + } + + if(titles.Count == 0) + break; + + OSDMap osdmembers = new OSDMap(); + foreach(GroupMembersData gmd in members) + { + OSDMap m = new OSDMap(); + if(gmd.OnlineStatus != null && gmd.OnlineStatus != "") + m["last_login"] = new OSDString(gmd.OnlineStatus); + if(gmd.AgentPowers != defaultPowers) + m["powers"] = new OSDString((gmd.AgentPowers).ToString("X")); + if(gmd.Title != null && titles.ContainsKey(gmd.Title) && titles[gmd.Title] != 0) + m["title"] = new OSDInteger(titles[gmd.Title]); + if(gmd.IsOwner) + m["owner"] = new OSDString("true"); + if(gmd.Contribution != 0) + m["donated_square_meters"] = new OSDInteger(gmd.Contribution); + + osdmembers[(gmd.AgentID).ToString()] = m; + } + + OSDMap osddefaults = new OSDMap(); + osddefaults["default_powers"] = new OSDString(defaultPowers.ToString("X")); + + resp["group_id"] = new OSDUUID(groupID); + resp["agent_id"] = new OSDUUID(m_AgentID); + resp["member_count"] = new OSDInteger(memberCount); + resp["defaults"] = osddefaults; + resp["titles"] = osdtitles; + resp["members"] = osdmembers; + + fail = false; + break; + } + + if(fail) + { + resp["group_id"] = new OSDUUID(groupID); + resp["agent_id"] = new OSDUUID(m_AgentID); + resp["member_count"] = new OSDInteger(0); + resp["defaults"] = new OSDMap(); + resp["titles"] = new OSDArray(); + resp["members"] = new OSDMap(); + } + + response = OSDParser.SerializeLLSDXmlString(resp); + return response; + } + + public string GetDisplayNames(string request, string path, + string param, IOSHttpRequest httpRequest, + IOSHttpResponse httpResponse) + { + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.Gone; + httpResponse.ContentType = "text/plain"; + + ScenePresence sp = m_Scene.GetScenePresence(m_AgentID); + if(sp == null || sp.IsDeleted) + return ""; + + if(sp.IsInTransit && !sp.IsInLocalTransit) + { + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.ServiceUnavailable; + httpResponse.AddHeader("Retry-After","30"); + return ""; + } + + NameValueCollection query = HttpUtility.ParseQueryString(httpRequest.Url.Query); + string[] ids = query.GetValues("ids"); + + Dictionary names = m_UserManager.GetUsersNames(ids); + + OSDMap osdReply = new OSDMap(); + OSDArray agents = new OSDArray(); + + osdReply["agents"] = agents; + foreach (KeyValuePair kvp in names) + { + if (string.IsNullOrEmpty(kvp.Value)) + continue; + if(kvp.Key == UUID.Zero) + continue; + + string[] parts = kvp.Value.Split(new char[] {' '}); + OSDMap osdname = new OSDMap(); + + // dont tell about unknown users, we can't send them back on Bad either + if(parts[0] == "Unknown") + continue; +/* + if(parts[0] == "Unknown") + { + osdname["display_name_next_update"] = OSD.FromDate(DateTime.UtcNow.AddHours(1)); + osdname["display_name_expires"] = OSD.FromDate(DateTime.UtcNow.AddHours(2)); + } + else +*/ + { + osdname["display_name_next_update"] = OSD.FromDate(DateTime.UtcNow.AddDays(8)); + osdname["display_name_expires"] = OSD.FromDate(DateTime.UtcNow.AddMonths(1)); + } + osdname["display_name"] = OSD.FromString(kvp.Value); + osdname["legacy_first_name"] = parts[0]; + osdname["legacy_last_name"] = parts[1]; + osdname["username"] = OSD.FromString(kvp.Value); + osdname["id"] = OSD.FromUUID(kvp.Key); + osdname["is_display_name_default"] = OSD.FromBoolean(true); + + agents.Add(osdname); + } + + // Full content request + httpResponse.StatusCode = (int)System.Net.HttpStatusCode.OK; + //httpResponse.ContentLength = ??; + httpResponse.ContentType = "application/llsd+xml"; + + string reply = OSDParser.SerializeLLSDXmlString(osdReply); + return reply; } } public class AssetUploader { + private static readonly ILog m_log = + LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public event UpLoadedAsset OnUpLoad; private UpLoadedAsset handlerUpLoad = null; @@ -1029,10 +1894,26 @@ namespace OpenSim.Region.ClientStack.Linden private string m_invType = String.Empty; private string m_assetType = String.Empty; + private int m_cost; + private string m_error = String.Empty; + + private Timer m_timeoutTimer = new Timer(); + private UUID m_texturesFolder; + private int m_nreqtextures; + private int m_nreqmeshs; + private int m_nreqinstances; + private bool m_IsAtestUpload; + + private int m_nextOwnerMask; + private int m_groupMask; + private int m_everyoneMask; + public AssetUploader(string assetName, string description, UUID assetID, UUID inventoryItem, UUID parentFolderID, string invType, string assetType, string path, - IHttpServer httpServer, bool dumpAssetsToFile) + IHttpServer httpServer, bool dumpAssetsToFile, + int totalCost, UUID texturesFolder, int nreqtextures, int nreqmeshs, int nreqinstances, + bool IsAtestUpload, int nextOwnerMask, int groupMask, int everyoneMask) { m_assetName = assetName; m_assetDes = description; @@ -1044,6 +1925,22 @@ namespace OpenSim.Region.ClientStack.Linden m_assetType = assetType; m_invType = invType; m_dumpAssetsToFile = dumpAssetsToFile; + m_cost = totalCost; + + m_texturesFolder = texturesFolder; + m_nreqtextures = nreqtextures; + m_nreqmeshs = nreqmeshs; + m_nreqinstances = nreqinstances; + m_IsAtestUpload = IsAtestUpload; + + m_timeoutTimer.Elapsed += TimedOut; + m_timeoutTimer.Interval = 120000; + m_timeoutTimer.AutoReset = false; + m_timeoutTimer.Start(); + + m_nextOwnerMask = nextOwnerMask; + m_groupMask = groupMask; + m_everyoneMask = everyoneMask; } /// @@ -1058,12 +1955,14 @@ namespace OpenSim.Region.ClientStack.Linden UUID inv = inventoryItemID; string res = String.Empty; LLSDAssetUploadComplete uploadComplete = new LLSDAssetUploadComplete(); +/* uploadComplete.new_asset = newAssetID.ToString(); uploadComplete.new_inventory_item = inv; uploadComplete.state = "complete"; res = LLSDHelpers.SerialiseLLSDReply(uploadComplete); - +*/ + m_timeoutTimer.Stop(); httpListener.RemoveStreamHandler("POST", uploaderPath); // TODO: probably make this a better set of extensions here @@ -1080,12 +1979,55 @@ namespace OpenSim.Region.ClientStack.Linden handlerUpLoad = OnUpLoad; if (handlerUpLoad != null) { - handlerUpLoad(m_assetName, m_assetDes, newAssetID, inv, parentFolder, data, m_invType, m_assetType); + handlerUpLoad(m_assetName, m_assetDes, newAssetID, inv, parentFolder, data, m_invType, m_assetType, + m_cost, m_texturesFolder, m_nreqtextures, m_nreqmeshs, m_nreqinstances, m_IsAtestUpload, + ref m_error, ref m_nextOwnerMask, ref m_groupMask, ref m_everyoneMask); } + uploadComplete.new_next_owner_mask = m_nextOwnerMask; + uploadComplete.new_group_mask = m_groupMask; + uploadComplete.new_everyone_mask = m_everyoneMask; + + if (m_IsAtestUpload) + { + LLSDAssetUploadError resperror = new LLSDAssetUploadError(); + resperror.message = "Upload SUCESSEFULL for testing purposes only. Other uses are prohibited. Item will not work after 48 hours or on other regions"; + resperror.identifier = inv; + + uploadComplete.error = resperror; + uploadComplete.state = "Upload4Testing"; + } + else + { + if (m_error == String.Empty) + { + uploadComplete.new_asset = newAssetID.ToString(); + uploadComplete.new_inventory_item = inv; + // if (m_texturesFolder != UUID.Zero) + // uploadComplete.new_texture_folder_id = m_texturesFolder; + uploadComplete.state = "complete"; + } + else + { + LLSDAssetUploadError resperror = new LLSDAssetUploadError(); + resperror.message = m_error; + resperror.identifier = inv; + + uploadComplete.error = resperror; + uploadComplete.state = "failed"; + } + } + + res = LLSDHelpers.SerialiseLLSDReply(uploadComplete); return res; } + private void TimedOut(object sender, ElapsedEventArgs args) + { + m_log.InfoFormat("[CAPS]: Removing URL and handler for timed out mesh upload"); + httpListener.RemoveStreamHandler("POST", uploaderPath); + } + ///Left this in and commented in case there are unforseen issues //private void SaveAssetToFile(string filename, byte[] data) //{ diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCapsModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCapsModule.cs index c241075..683c3d5 100644 --- a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCapsModule.cs +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/BunchOfCapsModule.cs @@ -63,7 +63,7 @@ namespace OpenSim.Region.ClientStack.Linden { } - public void Close() { } + public void Close() { } public void AddRegion(Scene scene) { @@ -80,11 +80,11 @@ namespace OpenSim.Region.ClientStack.Linden } public void PostInitialise() { } - #endregion + #endregion private void OnRegisterCaps(UUID agentID, Caps caps) { - new BunchOfCaps(m_Scene, caps); + new BunchOfCaps(m_Scene, agentID, caps); } } diff --git a/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs new file mode 100644 index 0000000..eb1ab45 --- /dev/null +++ b/OpenSim/Region/ClientStack/Linden/Caps/BunchOfCaps/MeshCost.cs @@ -0,0 +1,746 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +using System; +using System.IO; +using System.Collections; +using System.Collections.Generic; +using System.Text; + +using OpenMetaverse; +using OpenMetaverse.StructuredData; + +using OpenSim.Framework; +using OpenSim.Region.Framework; +using OpenSim.Region.Framework.Scenes; +using OpenSim.Framework.Capabilities; + +using ComponentAce.Compression.Libs.zlib; + +using OSDArray = OpenMetaverse.StructuredData.OSDArray; +using OSDMap = OpenMetaverse.StructuredData.OSDMap; + +using Nini.Config; + +namespace OpenSim.Region.ClientStack.Linden +{ + public struct ModelPrimLimits + { + + } + + public class ModelCost + { + + // upload fee defaults + // fees are normalized to 1.0 + // this parameters scale them to basic cost ( so 1.0 translates to 10 ) + + public float ModelMeshCostFactor = 0.0f; // scale total cost relative to basic (excluding textures) + public float ModelTextureCostFactor = 1.0f; // scale textures fee to basic. + public float ModelMinCostFactor = 0.0f; // 0.5f; // minimum total model free excluding textures + + // itens costs in normalized values + // ie will be multiplied by basicCost and factors above + public float primCreationCost = 0.002f; // extra cost for each prim creation overhead + // weigthed size to normalized cost + public float bytecost = 1e-5f; + + // mesh upload fees based on compressed data sizes + // several data sections are counted more that once + // to promote user optimization + // following parameters control how many extra times they are added + // to global size. + // LOD meshs + const float medSizeWth = 1f; // 2x + const float lowSizeWth = 1.5f; // 2.5x + const float lowestSizeWth = 2f; // 3x + // favor potencially physical optimized meshs versus automatic decomposition + const float physMeshSizeWth = 6f; // counts 7x + const float physHullSizeWth = 8f; // counts 9x + + // stream cost area factors + // more or less like SL + const float highLodFactor = 17.36f; + const float midLodFactor = 277.78f; + const float lowLodFactor = 1111.11f; + + // physics cost is below, identical to SL, assuming shape type convex + // server cost is below identical to SL assuming non scripted non physical object + + // internal + const int bytesPerCoord = 6; // 3 coords, 2 bytes per each + + // control prims dimensions + public float PrimScaleMin = 0.001f; + public float NonPhysicalPrimScaleMax = 256f; + public float PhysicalPrimScaleMax = 10f; + public int ObjectLinkedPartsMax = 512; + + + public ModelCost(Scene scene) + { + PrimScaleMin = scene.m_minNonphys; + NonPhysicalPrimScaleMax = scene.m_maxNonphys; + PhysicalPrimScaleMax = scene.m_maxPhys; + ObjectLinkedPartsMax = scene.m_linksetCapacity; + } + + public void Econfig(IConfig EconomyConfig) + { + ModelMeshCostFactor = EconomyConfig.GetFloat("MeshModelUploadCostFactor", ModelMeshCostFactor); + ModelTextureCostFactor = EconomyConfig.GetFloat("MeshModelUploadTextureCostFactor", ModelTextureCostFactor); + ModelMinCostFactor = EconomyConfig.GetFloat("MeshModelMinCostFactor", ModelMinCostFactor); + // next 2 are normalized so final cost is afected by modelUploadFactor above and normal cost + primCreationCost = EconomyConfig.GetFloat("ModelPrimCreationCost", primCreationCost); + bytecost = EconomyConfig.GetFloat("ModelMeshByteCost", bytecost); + } + + // storage for a single mesh asset cost parameters + private class ameshCostParam + { + // LOD sizes for size dependent streaming cost + public int highLODSize; + public int medLODSize; + public int lowLODSize; + public int lowestLODSize; + // normalized fee based on compressed data sizes + public float costFee; + // physics cost + public float physicsCost; + } + + // calculates a mesh model costs + // returns false on error, with a reason on parameter error + // resources input LLSD request + // basicCost input region assets upload cost + // totalcost returns model total upload fee + // meshcostdata returns detailed costs for viewer + // avatarSkeleton if mesh includes a avatar skeleton + // useAvatarCollider if we should use physics mesh for avatar + public bool MeshModelCost(LLSDAssetResource resources, int basicCost, out int totalcost, + LLSDAssetUploadResponseData meshcostdata, out string error, ref string warning) + { + totalcost = 0; + error = string.Empty; + + bool avatarSkeleton = false; + + if (resources == null || + resources.instance_list == null || + resources.instance_list.Array.Count == 0) + { + error = "missing model information."; + return false; + } + + int numberInstances = resources.instance_list.Array.Count; + + if (ObjectLinkedPartsMax != 0 && numberInstances > ObjectLinkedPartsMax) + { + error = "Model would have more than " + ObjectLinkedPartsMax.ToString() + " linked prims"; + return false; + } + + meshcostdata.model_streaming_cost = 0.0; + meshcostdata.simulation_cost = 0.0; + meshcostdata.physics_cost = 0.0; + meshcostdata.resource_cost = 0.0; + + meshcostdata.upload_price_breakdown.mesh_instance = 0; + meshcostdata.upload_price_breakdown.mesh_physics = 0; + meshcostdata.upload_price_breakdown.mesh_streaming = 0; + meshcostdata.upload_price_breakdown.model = 0; + + int itmp; + + // textures cost + if (resources.texture_list != null && resources.texture_list.Array.Count > 0) + { + float textures_cost = (float)(resources.texture_list.Array.Count * basicCost); + textures_cost *= ModelTextureCostFactor; + + itmp = (int)(textures_cost + 0.5f); // round + meshcostdata.upload_price_breakdown.texture = itmp; + totalcost += itmp; + } + + // meshs assets cost + float meshsfee = 0; + int numberMeshs = 0; + bool haveMeshs = false; + + bool curskeleton; + bool curAvatarPhys; + + List meshsCosts = new List(); + + if (resources.mesh_list != null && resources.mesh_list.Array.Count > 0) + { + numberMeshs = resources.mesh_list.Array.Count; + + for (int i = 0; i < numberMeshs; i++) + { + ameshCostParam curCost = new ameshCostParam(); + byte[] data = (byte[])resources.mesh_list.Array[i]; + + if (!MeshCost(data, curCost,out curskeleton, out curAvatarPhys, out error)) + { + return false; + } + + if (curskeleton) + { + if (avatarSkeleton) + { + error = "model can only contain a avatar skeleton"; + return false; + } + avatarSkeleton = true; + } + meshsCosts.Add(curCost); + meshsfee += curCost.costFee; + } + haveMeshs = true; + } + + // instances (prims) cost + + + int mesh; + int skipedSmall = 0; + for (int i = 0; i < numberInstances; i++) + { + Hashtable inst = (Hashtable)resources.instance_list.Array[i]; + + ArrayList ascale = (ArrayList)inst["scale"]; + Vector3 scale; + double tmp; + tmp = (double)ascale[0]; + scale.X = (float)tmp; + tmp = (double)ascale[1]; + scale.Y = (float)tmp; + tmp = (double)ascale[2]; + scale.Z = (float)tmp; + + if (scale.X < PrimScaleMin || scale.Y < PrimScaleMin || scale.Z < PrimScaleMin) + { + skipedSmall++; + continue; + } + + if (scale.X > NonPhysicalPrimScaleMax || scale.Y > NonPhysicalPrimScaleMax || scale.Z > NonPhysicalPrimScaleMax) + { + error = "Model contains parts with sides larger than " + NonPhysicalPrimScaleMax.ToString() + "m. Please ajust scale"; + return false; + } + + if (haveMeshs && inst.ContainsKey("mesh")) + { + mesh = (int)inst["mesh"]; + + if (mesh >= numberMeshs) + { + error = "Incoerent model information."; + return false; + } + + // streamming cost + + float sqdiam = scale.LengthSquared(); + + ameshCostParam curCost = meshsCosts[mesh]; + float mesh_streaming = streamingCost(curCost, sqdiam); + + meshcostdata.model_streaming_cost += mesh_streaming; + meshcostdata.physics_cost += curCost.physicsCost; + } + else // instance as no mesh ?? + { + // to do later if needed + meshcostdata.model_streaming_cost += 0.5f; + meshcostdata.physics_cost += 1.0f; + } + + // assume unscripted and static prim server cost + meshcostdata.simulation_cost += 0.5f; + // charge for prims creation + meshsfee += primCreationCost; + } + + if (skipedSmall > 0) + { + if (skipedSmall > numberInstances / 2) + { + error = "Model contains too many prims smaller than " + PrimScaleMin.ToString() + + "m minimum allowed size. Please check scalling"; + return false; + } + else + warning += skipedSmall.ToString() + " of the requested " +numberInstances.ToString() + + " model prims will not upload because they are smaller than " + PrimScaleMin.ToString() + + "m minimum allowed size. Please check scalling "; + } + + if (meshcostdata.physics_cost <= meshcostdata.model_streaming_cost) + meshcostdata.resource_cost = meshcostdata.model_streaming_cost; + else + meshcostdata.resource_cost = meshcostdata.physics_cost; + + if (meshcostdata.resource_cost < meshcostdata.simulation_cost) + meshcostdata.resource_cost = meshcostdata.simulation_cost; + + // scale cost + // at this point a cost of 1.0 whould mean basic cost + meshsfee *= ModelMeshCostFactor; + + if (meshsfee < ModelMinCostFactor) + meshsfee = ModelMinCostFactor; + + // actually scale it to basic cost + meshsfee *= (float)basicCost; + + meshsfee += 0.5f; // rounding + + totalcost += (int)meshsfee; + + // breakdown prices + // don't seem to be in use so removed code for now + + return true; + } + + // single mesh asset cost + private bool MeshCost(byte[] data, ameshCostParam cost,out bool skeleton, out bool avatarPhys, out string error) + { + cost.highLODSize = 0; + cost.medLODSize = 0; + cost.lowLODSize = 0; + cost.lowestLODSize = 0; + cost.physicsCost = 0.0f; + cost.costFee = 0.0f; + + error = string.Empty; + + skeleton = false; + avatarPhys = false; + + if (data == null || data.Length == 0) + { + error = "Missing model information."; + return false; + } + + OSD meshOsd = null; + int start = 0; + + error = "Invalid model data"; + + using (MemoryStream ms = new MemoryStream(data)) + { + try + { + OSD osd = OSDParser.DeserializeLLSDBinary(ms); + if (osd is OSDMap) + meshOsd = (OSDMap)osd; + else + return false; + } + catch + { + return false; + } + start = (int)ms.Position; + } + + OSDMap map = (OSDMap)meshOsd; + OSDMap tmpmap; + + int highlod_size = 0; + int medlod_size = 0; + int lowlod_size = 0; + int lowestlod_size = 0; + int skin_size = 0; + + int hulls_size = 0; + int phys_nhulls; + int phys_hullsvertices = 0; + + int physmesh_size = 0; + int phys_ntriangles = 0; + + int submesh_offset = -1; + + if (map.ContainsKey("skeleton")) + { + tmpmap = (OSDMap)map["skeleton"]; + if (tmpmap.ContainsKey("offset") && tmpmap.ContainsKey("size")) + { + int sksize = tmpmap["size"].AsInteger(); + if(sksize > 0) + skeleton = true; + } + } + + if (map.ContainsKey("physics_convex")) + { + tmpmap = (OSDMap)map["physics_convex"]; + if (tmpmap.ContainsKey("offset")) + submesh_offset = tmpmap["offset"].AsInteger() + start; + if (tmpmap.ContainsKey("size")) + hulls_size = tmpmap["size"].AsInteger(); + } + + if (submesh_offset < 0 || hulls_size == 0) + { + error = "Missing physics_convex block"; + return false; + } + + if (!hulls(data, submesh_offset, hulls_size, out phys_hullsvertices, out phys_nhulls)) + { + error = "Bad physics_convex block"; + return false; + } + + submesh_offset = -1; + + // only look for LOD meshs sizes + + if (map.ContainsKey("high_lod")) + { + tmpmap = (OSDMap)map["high_lod"]; + // see at least if there is a offset for this one + if (tmpmap.ContainsKey("offset")) + submesh_offset = tmpmap["offset"].AsInteger() + start; + if (tmpmap.ContainsKey("size")) + highlod_size = tmpmap["size"].AsInteger(); + } + + if (submesh_offset < 0 || highlod_size <= 0) + { + error = "Missing high_lod block"; + return false; + } + + bool haveprev = true; + + if (map.ContainsKey("medium_lod")) + { + tmpmap = (OSDMap)map["medium_lod"]; + if (tmpmap.ContainsKey("size")) + medlod_size = tmpmap["size"].AsInteger(); + else + haveprev = false; + } + + if (haveprev && map.ContainsKey("low_lod")) + { + tmpmap = (OSDMap)map["low_lod"]; + if (tmpmap.ContainsKey("size")) + lowlod_size = tmpmap["size"].AsInteger(); + else + haveprev = false; + } + + if (haveprev && map.ContainsKey("lowest_lod")) + { + tmpmap = (OSDMap)map["lowest_lod"]; + if (tmpmap.ContainsKey("size")) + lowestlod_size = tmpmap["size"].AsInteger(); + } + + if (map.ContainsKey("skin")) + { + tmpmap = (OSDMap)map["skin"]; + if (tmpmap.ContainsKey("size")) + skin_size = tmpmap["size"].AsInteger(); + } + + cost.highLODSize = highlod_size; + cost.medLODSize = medlod_size; + cost.lowLODSize = lowlod_size; + cost.lowestLODSize = lowestlod_size; + + submesh_offset = -1; + + tmpmap = null; + if(map.ContainsKey("physics_mesh")) + tmpmap = (OSDMap)map["physics_mesh"]; + else if (map.ContainsKey("physics_shape")) // old naming + tmpmap = (OSDMap)map["physics_shape"]; + + if(tmpmap != null) + { + if (tmpmap.ContainsKey("offset")) + submesh_offset = tmpmap["offset"].AsInteger() + start; + if (tmpmap.ContainsKey("size")) + physmesh_size = tmpmap["size"].AsInteger(); + + if (submesh_offset >= 0 || physmesh_size > 0) + { + + if (!submesh(data, submesh_offset, physmesh_size, out phys_ntriangles)) + { + error = "Model data parsing error"; + return false; + } + } + } + + // upload is done in convex shape type so only one hull + phys_hullsvertices++; + cost.physicsCost = 0.04f * phys_hullsvertices; + + float sfee; + + sfee = data.Length; // start with total compressed data size + + // penalize lod meshs that should be more builder optimized + sfee += medSizeWth * medlod_size; + sfee += lowSizeWth * lowlod_size; + sfee += lowestSizeWth * lowlod_size; + + // physics + // favor potencial optimized meshs versus automatic decomposition + if (physmesh_size != 0) + sfee += physMeshSizeWth * (physmesh_size + hulls_size / 4); // reduce cost of mandatory convex hull + else + sfee += physHullSizeWth * hulls_size; + + // bytes to money + sfee *= bytecost; + + cost.costFee = sfee; + return true; + } + + // parses a LOD or physics mesh component + private bool submesh(byte[] data, int offset, int size, out int ntriangles) + { + ntriangles = 0; + + OSD decodedMeshOsd = new OSD(); + byte[] meshBytes = new byte[size]; + System.Buffer.BlockCopy(data, offset, meshBytes, 0, size); + try + { + using (MemoryStream inMs = new MemoryStream(meshBytes)) + { + using (MemoryStream outMs = new MemoryStream()) + { + using (ZOutputStream zOut = new ZOutputStream(outMs)) + { + byte[] readBuffer = new byte[4096]; + int readLen = 0; + while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0) + { + zOut.Write(readBuffer, 0, readLen); + } + zOut.Flush(); + outMs.Seek(0, SeekOrigin.Begin); + + byte[] decompressedBuf = outMs.GetBuffer(); + decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf); + } + } + } + } + catch + { + return false; + } + + OSDArray decodedMeshOsdArray = null; + + byte[] dummy; + + decodedMeshOsdArray = (OSDArray)decodedMeshOsd; + foreach (OSD subMeshOsd in decodedMeshOsdArray) + { + if (subMeshOsd is OSDMap) + { + OSDMap subtmpmap = (OSDMap)subMeshOsd; + if (subtmpmap.ContainsKey("NoGeometry") && ((OSDBoolean)subtmpmap["NoGeometry"])) + continue; + + if (!subtmpmap.ContainsKey("Position")) + return false; + + if (subtmpmap.ContainsKey("TriangleList")) + { + dummy = subtmpmap["TriangleList"].AsBinary(); + ntriangles += dummy.Length / bytesPerCoord; + } + else + return false; + } + } + + return true; + } + + // parses convex hulls component + private bool hulls(byte[] data, int offset, int size, out int nvertices, out int nhulls) + { + nvertices = 0; + nhulls = 1; + + OSD decodedMeshOsd = new OSD(); + byte[] meshBytes = new byte[size]; + System.Buffer.BlockCopy(data, offset, meshBytes, 0, size); + try + { + using (MemoryStream inMs = new MemoryStream(meshBytes)) + { + using (MemoryStream outMs = new MemoryStream()) + { + using (ZOutputStream zOut = new ZOutputStream(outMs)) + { + byte[] readBuffer = new byte[4096]; + int readLen = 0; + while ((readLen = inMs.Read(readBuffer, 0, readBuffer.Length)) > 0) + { + zOut.Write(readBuffer, 0, readLen); + } + zOut.Flush(); + outMs.Seek(0, SeekOrigin.Begin); + + byte[] decompressedBuf = outMs.GetBuffer(); + decodedMeshOsd = OSDParser.DeserializeLLSDBinary(decompressedBuf); + } + } + } + } + catch + { + return false; + } + + OSDMap cmap = (OSDMap)decodedMeshOsd; + if (cmap == null) + return false; + + byte[] dummy; + + // must have one of this + if (cmap.ContainsKey("BoundingVerts")) + { + dummy = cmap["BoundingVerts"].AsBinary(); + nvertices = dummy.Length / bytesPerCoord; + } + else + return false; + +/* upload is done with convex shape type + if (cmap.ContainsKey("HullList")) + { + dummy = cmap["HullList"].AsBinary(); + nhulls += dummy.Length; + } + + + if (cmap.ContainsKey("Positions")) + { + dummy = cmap["Positions"].AsBinary(); + nvertices = dummy.Length / bytesPerCoord; + } + */ + + return true; + } + + // returns streaming cost from on mesh LODs sizes in curCost and square of prim size length + private float streamingCost(ameshCostParam curCost, float sqdiam) + { + // compute efective areas + float ma = 262144f; + + float mh = sqdiam * highLodFactor; + if (mh > ma) + mh = ma; + float mm = sqdiam * midLodFactor; + if (mm > ma) + mm = ma; + + float ml = sqdiam * lowLodFactor; + if (ml > ma) + ml = ma; + + float mlst = ma; + + mlst -= ml; + ml -= mm; + mm -= mh; + + if (mlst < 1.0f) + mlst = 1.0f; + if (ml < 1.0f) + ml = 1.0f; + if (mm < 1.0f) + mm = 1.0f; + if (mh < 1.0f) + mh = 1.0f; + + ma = mlst + ml + mm + mh; + + // get LODs compressed sizes + // giving 384 bytes bonus + int lst = curCost.lowestLODSize - 384; + int l = curCost.lowLODSize - 384; + int m = curCost.medLODSize - 384; + int h = curCost.highLODSize - 384; + + // use previus higher LOD size on missing ones + if (m <= 0) + m = h; + if (l <= 0) + l = m; + if (lst <= 0) + lst = l; + + // force minumum sizes + if (lst < 16) + lst = 16; + if (l < 16) + l = 16; + if (m < 16) + m = 16; + if (h < 16) + h = 16; + + // compute cost weighted by relative effective areas + float cost = (float)lst * mlst + (float)l * ml + (float)m * mm + (float)h * mh; + cost /= ma; + + cost *= 0.004f; // overall tunning parameter + + return cost; + } + } +} -- cgit v1.1