/* Copyright 2014 ROBLOX Corporation, All Rights Reserved */ #include "stdafx.h" #include "V8DataModel/PartOperationAsset.h" #include "V8DataModel/PartOperation.h" #include "V8DataModel/GameBasicSettings.h" #include "G3D/G3dMath.h" #include "G3D/CollisionDetection.h" #include "Network/Players.h" #include "Reflection/Reflection.h" #include "RBX/RbxTime.h" #include "Util/BinaryString.h" #include "Util/NormalId.h" #include "Util/RobloxGoogleAnalytics.h" #include "Util/SurfaceType.h" #include "Util/stringbuffer.h" #include "V8DataModel/PartInstance.h" #include "V8DataModel/Workspace.h" #include "V8DataModel/CSGMesh.h" #include "V8World/ContactManager.h" #include "V8World/Primitive.h" #include "V8World/World.h" #include "V8World/BulletGeometryPoolObjects.h" #include "v8datamodel/CSGDictionaryService.h" #include "V8DataModel/NonReplicatedCSGDictionaryService.h" #include "V8DataModel/FlyweightService.h" #include "V8World/TriangleMesh.h" #include "V8DataModel/ContentProvider.h" #include "V8Xml/Serializer.h" #include "V8Xml/SerializerBinary.h" #include "V8DataModel/ContentProvider.h" #include "V8DataModel/SolidModelContentProvider.h" FASTFLAGVARIABLE(CSGFixForNoChildData, true) namespace RBX { using namespace Reflection; const Reflection::PropDescriptor PartOperationAsset::desc_ChildData("ChildData", category_Data, &PartOperationAsset::getChildData, &PartOperationAsset::setChildData, Reflection::PropertyDescriptor::CLUSTER, Security::Roblox); const Reflection::PropDescriptor PartOperationAsset::desc_MeshData("MeshData", category_Data, &PartOperationAsset::getMeshData, &PartOperationAsset::setMeshData, Reflection::PropertyDescriptor::STREAMING, Security::Roblox); const char* const sPartOperationAsset = "PartOperationAsset"; void setAssetOnMatchingPartOperations(shared_ptr descendant, const ContentId& url, const BinaryString& key) { if(PartOperation* partOperation = RBX::Instance::fastDynamicCast(descendant.get())) { if (partOperation->hasAsset()) return; if (partOperation->getChildData() == key) { partOperation->setAssetId(url); BinaryString noValue; partOperation->setChildData(noValue); partOperation->setMeshData(noValue); } } } void publishPartOperations(shared_ptr descendant, RBX::Time startTime, const int timeoutMills) { if (timeoutMills != -1 && (startTime - RBX::Time::nowFast()).msec() > timeoutMills) return; if(shared_ptr partOperation = Instance::fastSharedDynamicCast(descendant)) { if (partOperation->hasAsset()) { const BinaryString childKey = partOperation->getChildData(); const BinaryString meshKey = partOperation->getMeshData(); if (meshKey.value().empty() && childKey.value().empty()) return; RBX::ContentId contentId = partOperation->getAssetId(); RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_STUDIO, "RemoveLeftoverCSGData", contentId.c_str()); DataModel* dataModel = DataModel::get(partOperation.get()); if (CacheableContentProvider* mcp = ServiceProvider::create(dataModel)) { if (boost::shared_ptr partOperationAsset = boost::static_pointer_cast(mcp->blockingRequestContent(contentId, true))) { //ChildData if (!childKey.value().empty() && !partOperationAsset->getChildData().value().empty()) { NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::find(dataModel); if (nrDictionaryService->isHashKey(childKey.value())) nrDictionaryService->retrieveData(*partOperation); partOperation->setChildData(BinaryString("")); } //MeshData if (!meshKey.value().empty() && !partOperationAsset->getMeshData().value().empty()) { CSGDictionaryService* dictionaryService = ServiceProvider::find< CSGDictionaryService >(dataModel); if (dictionaryService->isHashKey(meshKey.value())) dictionaryService->retrieveMeshData(*partOperation); partOperation->setMeshData(BinaryString("")); } } } return; } DataModel* dataModel = DataModel::get(partOperation.get()); CSGDictionaryService* dictionaryService = ServiceProvider::create< CSGDictionaryService >(dataModel); NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::create(dataModel); ContentProvider* contentProvider = ServiceProvider::create(dataModel); const BinaryString meshKey = partOperation->getMeshData(); const BinaryString childKey = partOperation->getChildData(); if (meshKey.value().empty() || childKey.value().empty()) return; if (!dictionaryService->isHashKey(meshKey.value()) || !nrDictionaryService->isHashKey(childKey.value())) { RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_STUDIO, "PublishCSGFailure", "HashKeyNotFound"); return; } const BinaryString meshData = dictionaryService->peekAtData(meshKey); BinaryString childData = nrDictionaryService->peekAtData(childKey); if (childData.value().empty()) childData = dictionaryService->peekAtData(childKey); bool validChildData = !childData.value().empty(); shared_ptr partOperationAsset = Creatable::create(); partOperationAsset->setMeshData(meshData); partOperationAsset->setChildData(childData); std::stringstream stream; RBX::Instances instances; instances.push_back(partOperationAsset); SerializerBinary::serialize(stream, instances); std::string baseUrl = contentProvider->getBaseUrl(); RBX::Http http(RBX::format("%s/ide/publish/uploadnewasset?assetTypeName=SolidModel&name=SolidModel&description=SolidModel&isPublic=True&genreTypeId=1&allowComments=False", baseUrl.c_str())); try { std::string response; http.post(stream, RBX::Http::kContentTypeApplicationXml, true, response); int newAssetId; std::stringstream istream(response); istream >> newAssetId; std::string assetId; assetId = RBX::format("%s/asset/?id=%d", baseUrl.c_str(), newAssetId); ContentId contentId = RBX::ContentId(assetId.c_str()); partOperation->setAssetId(contentId); BinaryString noValue; partOperation->setChildData(noValue); partOperation->setMeshData(noValue); if (validChildData || !FFlag::CSGFixForNoChildData) dataModel->visitDescendants(boost::bind(&setAssetOnMatchingPartOperations, _1, contentId, childKey)); if (validChildData || !FFlag::CSGFixForNoChildData) dictionaryService->removeStringData(meshKey); nrDictionaryService->removeStringData(childKey); } catch(std::exception&) { throw DataModel::SerializationException("Failed to upload union. Exceeded limit. Try again in a few minutes."); } // awagnerTODO: pass childata to LRU cache in content provider } } bool PartOperationAsset::publishAll(DataModel* dataModel, int timeoutMills) { RBX::Time startPublish = RBX::Time::nowFast(); dataModel->visitDescendants(boost::bind(&publishPartOperations, _1, startPublish, timeoutMills)); RobloxGoogleAnalytics::trackUserTiming(GA_CATEGORY_STUDIO, "SolidModelPublishAll", static_cast((RBX::Time::nowFast() - startPublish).msec())); CSGDictionaryService* dictionaryService = ServiceProvider::find< CSGDictionaryService >(dataModel); NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::find(dataModel); dictionaryService->clean(); nrDictionaryService->clean(); return true; } bool PartOperationAsset::publishSelection(DataModel* dataModel, int timeoutMills) { RBX::Time startPublish = RBX::Time::nowFast(); RBX::Selection* sel = RBX::ServiceProvider::create(dataModel); for (RBX::Instances::const_iterator iter = sel->begin(); iter != sel->end(); ++iter) { publishPartOperations(*iter, startPublish, timeoutMills); } RobloxGoogleAnalytics::trackUserTiming(GA_CATEGORY_STUDIO, "SolidModelPublishSelection", static_cast((RBX::Time::nowFast() - startPublish).msec())); CSGDictionaryService* dictionaryService = ServiceProvider::find< CSGDictionaryService >(dataModel); NonReplicatedCSGDictionaryService* nrDictionaryService = ServiceProvider::find(dataModel); dictionaryService->clean(); nrDictionaryService->clean(); return true; } } //namespace