// 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<ameshCostParam> meshsCosts = new List<ameshCostParam>(); 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; }
/// <summary> /// /// </summary> /// <param name="llsdRequest"></param> /// <returns></returns> public LLSDAssetUploadResponse NewAgentInventoryRequest(LLSDAssetUploadRequest llsdRequest) { //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; m_Scene.TryGetScenePresence(m_HostCapsObj.AgentID, out avatar); // check user level if (avatar != null) { if (avatar.UserLevel < m_levelUpload) { 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 test upload and funds if (client != null) { IMoneyModule mm = m_Scene.RequestModuleInterface<IMoneyModule>(); int baseCost = 0; if (mm != null) baseCost = mm.UploadCharge; string warning = String.Empty; if (llsdRequest.asset_type == "mesh") { string error; int modelcost; if (!m_ModelCost.MeshModelCost(llsdRequest.asset_resources, baseCost, out modelcost, meshcostdata, out error, ref warning)) { 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 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"); 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, 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, 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; 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; }