// d9mz - This file makes me really sad #if defined(_WIN32) || defined(_WIN64) #include #endif #include "rapidjson/document.h" #include "generated/soapRCCServiceSoapService.h" #include "ProcessPerfCounter.hpp" #include "Debug.hpp" #include "Utility/ProtectedString.hpp" #include "Utility/StandardOut.hpp" #include "boost/noncopyable.hpp" #include "boost/thread/xtime.hpp" #include "boost/foreach.hpp" #include "DataModel/Workspace.hpp" #include "DataModel/ContentProvider.hpp" #include "DataModel/DataModel.hpp" #include "DataModel/DebugSettings.hpp" #include "DataModel/PhysicsSettings.hpp" #include "DataModel/FastLogSettings.hpp" #include "DataModel/GameBasicSettings.hpp" #include "Script/LuaSettings.hpp" #include "DataModel/GameSettings.hpp" #include "DataModel/MarketplaceService.hpp" #include "DataModel/AdService.hpp" #include "DataModel/CSGDictionaryService.hpp" #include "DataModel/NonReplicatedCSGDictionaryService.hpp" #include "DataModel/Message.hpp" #include "Utility/RobloxServicesTools.hpp" #include "Script/ScriptContext.hpp" #include "ThumbnailGenerator.hpp" #include #include #include "DataModel/factoryregistration.hpp" #include "Utility/Profiling.hpp" #include "Utility/SoundService.hpp" #include "Utility/Guid.hpp" #include "Utility/Http.hpp" #include "Utility/Statistics.hpp" #include "Utility/rbxrandom.hpp" #include "API.hpp" #include "Utility/AyaService.hpp" #include #include "Base/ViewBase.hpp" #include "Utility/FileSystem.hpp" #include "Players.hpp" #include "Xml/XmlSerializer.hpp" #include "Xml/WebParser.hpp" #include "Utility/RobloxServicesTools.hpp" #include "Utility/Utilities.hpp" #include "ChatFilter.hpp" #include "WebChatFilter.hpp" #include "DataModel/ContentProvider.hpp" #include "CountersClient.hpp" #include "SimpleJSON.hpp" #include "AyaFormat.hpp" #if !defined(__linux) && !defined(__APPLE__) #include "VersionInfo.hpp" #include "DumpErrorUploader.hpp" #endif #include "Utility/Analytics.hpp" #include "boost/date_time/posix_time/posix_time.hpp" #include #if !defined(__linux) && !defined(__APPLE__) #include #endif long diagCount = 0; long batchJobCount = 0; long openJobCount = 0; long closeJobCount = 0; long helloWorldCount = 0; long getVersionCount = 0; long renewLeaseCount = 0; long executeCount = 0; long getExpirationCount = 0; long getStatusCount = 0; long getAllJobsCount = 0; long closeExpiredJobsCount = 0; long closeAllJobsCount = 0; // #define DIAGNOSTICS #ifdef DIAGNOSTICS #define BEGIN_PRINT(func, msg) \ static int func = 0; \ int counter = func++; \ Aya::StandardOut::singleton()->printf(Aya::MESSAGE_INFO, "Begin-%s%d\r\n", msg, counter) #define END_PRINT(func, msg) Aya::StandardOut::singleton()->printf(Aya::MESSAGE_INFO, "End---%s%d\r\n", msg, counter) #else #define BEGIN_PRINT(func, msg) #define END_PRINT(func, msg) #endif LOGVARIABLE(RCCServiceInit, 1); LOGVARIABLE(RCCServiceJobs, 1); LOGVARIABLE(RCCDataModelInit, 1); // See TaskScheduler::ThreadPoolConfig enum for valid values DYNAMIC_FASTINTVARIABLE(TaskSchedulerThreadCountEnum, 1) DYNAMIC_FASTFLAGVARIABLE(DebugCrashOnFailToLoadClientSettings, false) DYNAMIC_FASTFLAGVARIABLE(UseNewSecurityKeyApi, false); DYNAMIC_FASTFLAGVARIABLE(UseNewMemHashApi, false); DYNAMIC_FASTSTRINGVARIABLE(MemHashConfig, ""); FASTINTVARIABLE(RCCServiceThreadCount, Aya::TaskScheduler::Threads1) DYNAMIC_FASTFLAGVARIABLE(US30476, false); FASTFLAGVARIABLE(UseDataDomain, true); FASTFLAGVARIABLE(Dep, true) namespace Aya { class Explosion; class Cofm; class NormalBreakConnector; class ContactConnector; class RevoluteLink; class SimBody; class BallBallContact; class BallBlockContact; class BlockBlockContact; } // namespace Aya class CrashAfterTimeout { shared_ptr event; shared_ptr mutex; static void run(shared_ptr event, int timeount, shared_ptr mutex) { boost::xtime xt; boost::xtime_get(&xt, boost::TIME_UTC_); xt.sec += static_cast(timeount); boost::mutex::scoped_lock lock(*mutex); bool set = event->timed_wait(lock, xt); if (!set) AYACRASH(); } public: CrashAfterTimeout(int seconds) : event(new boost::condition_variable_any()) , mutex(new boost::mutex()) { boost::thread thread(boost::bind(&CrashAfterTimeout::run, event, seconds, mutex)); } ~CrashAfterTimeout() { boost::mutex::scoped_lock lock(*mutex); event->notify_all(); } }; class RCCServiceSettings : public Aya::FastLogJSON { public: START_DATA_MAP(RCCServiceSettings); DECLARE_DATA_STRING(WindowsMD5); DECLARE_DATA_STRING(WindowsPlayerBetaMD5); DECLARE_DATA_STRING(MacMD5); DECLARE_DATA_INT(SecurityDataTimer); DECLARE_DATA_INT(ClientSettingsTimer); END_DATA_MAP(); }; class SecurityDataUpdater { std::string apiUrl; protected: std::string data; virtual void processDataArray(shared_ptr dataArray) = 0; bool fetchData() { std::string newData; try { Aya::Http request(apiUrl); request.get(newData); // no need to continue if data did not change if (newData == data) return false; } catch (std::exception& ex) { Aya::StandardOut::singleton()->printf( Aya::MESSAGE_WARNING, "SecurityDataUpdater failed to fetch data from %s, %s", apiUrl.c_str(), ex.what()); return false; } data = newData; return true; } public: SecurityDataUpdater(const std::string& url) : apiUrl(url) { } virtual ~SecurityDataUpdater() {} void run() { if (!fetchData()) return; shared_ptr jsonResult(Aya::make_shared()); if (Aya::WebParser::parseJSONTable(data, jsonResult)) { Aya::Reflection::ValueTable::const_iterator iter = jsonResult->find("data"); if (iter != jsonResult->end()) { shared_ptr dataArray = iter->second.cast>(); processDataArray(dataArray); } } } }; class MD5Updater : public SecurityDataUpdater { protected: void processDataArray(shared_ptr dataArray) { std::set hashes; for (Aya::Reflection::ValueArray::const_iterator it = dataArray->begin(); it != dataArray->end(); ++it) { std::string value = it->get(); hashes.insert(value); } // always add hash for ios hashes.insert("ios,ios"); Aya::Network::Players::setGoldenHashes2(hashes); } public: MD5Updater(const std::string& url) : SecurityDataUpdater(url) { } ~MD5Updater() {} }; class SecurityKeyUpdater : public SecurityDataUpdater { protected: void processDataArray(shared_ptr dataArray) { std::vector versions; for (Aya::Reflection::ValueArray::const_iterator it = dataArray->begin(); it != dataArray->end(); ++it) { // version = data + salt std::string value = it->get(); std::string version = Aya::sha1(value + "askljfLUZF"); versions.push_back(version); } Aya::Network::setSecurityVersions(versions); } public: SecurityKeyUpdater(const std::string& url) : SecurityDataUpdater(url) { } ~SecurityKeyUpdater() {} }; class MemHashUpdater : public SecurityDataUpdater { public: // this is public until we get a web api to do this better. static void populateMemHashConfigs(Aya::Network::MemHashConfigs& hashConfigs, const std::string& cfgStr) { hashConfigs.push_back(Aya::Network::MemHashVector()); Aya::Network::MemHashVector& thisHashVector = hashConfigs.back(); std::vector hashStrInfo; boost::split(hashStrInfo, cfgStr, boost::is_any_of(";")); for (std::vector::const_iterator hpIt = hashStrInfo.begin(); hpIt != hashStrInfo.end(); ++hpIt) { std::vector args; boost::split(args, *hpIt, boost::is_any_of(",")); if (args.size() >= 3) { Aya::Network::MemHash thisHash; thisHash.checkIdx = boost::lexical_cast(args[0]); thisHash.value = boost::lexical_cast(args[1]); thisHash.failMask = boost::lexical_cast(args[2]); thisHashVector.push_back(thisHash); } } }; protected: void processDataArray(shared_ptr dataArray) { Aya::Network::MemHashConfigs hashConfigs; for (Aya::Reflection::ValueArray::const_iterator it = dataArray->begin(); it != dataArray->end(); ++it) { populateMemHashConfigs(hashConfigs, it->get()); } Aya::Network::Players::setGoldMemHashes(hashConfigs); } public: MemHashUpdater(const std::string& url) : SecurityDataUpdater(url) { } ~MemHashUpdater() {} }; class RCCServiceDynamicSettings : public Aya::FastLogJSON { typedef std::map FVariables; FVariables fVars; public: virtual void ProcessVariable(const std::string& valueName, const std::string& valueData, bool dynamic) { AYAASSERT(dynamic); fVars.insert(std::make_pair(valueName, valueData)); } virtual bool DefaultHandler(const std::string& valueName, const std::string& valueData) { // only process dynamic flags and logs if (valueName[0] == 'D') return FastLogJSON::DefaultHandler(valueName, valueData); return false; } void UpdateSettings() { for (FVariables::iterator i = fVars.begin(); i != fVars.end(); i++) FLog::SetValue(i->first, i->second, FASTVARTYPE_DYNAMIC, false); fVars.clear(); } }; DATA_MAP_IMPL_START(RCCServiceSettings) IMPL_DATA(WindowsMD5, ""); IMPL_DATA(MacMD5, ""); IMPL_DATA(WindowsPlayerBetaMD5, ""); IMPL_DATA(SecurityDataTimer, 300); // in seconds, default 5 min IMPL_DATA(ClientSettingsTimer, 120); // 2 min IMPL_DATA(HttpUseCurlPercentageRCC, 0); // do not use CURL by default DATA_MAP_IMPL_END() #if !defined(__linux) && !defined(__APPLE__) static boost::scoped_ptr mainLogManager(new MainLogManager("Kiseki Web Service", ".dmp", ".crashevent")); #endif #ifdef AYA_TEST_BUILD std::string RCCServiceSettingsKeyOverwrite; #endif class CWebService { #if !defined(__linux) && !defined(__APPLE__) boost::shared_ptr s_perfCounter; ATL::CEvent doneEvent; #endif boost::scoped_ptr perfData; boost::scoped_ptr fetchSecurityDataThread; boost::scoped_ptr fetchClientSettingsThread; RCCServiceSettings rccSettings; RCCServiceDynamicSettings rccDynamicSettings; std::string settingsKey; std::string securityVersionData; std::string clientSettingsData; std::string thumbnailSettingsData; bool isThumbnailer; boost::scoped_ptr md5Updater; boost::scoped_ptr securityKeyUpdater; boost::scoped_ptr memHashUpdater; boost::scoped_ptr counters; public: struct JobItem : boost::noncopyable { enum JobItemRunStatus { RUNNING_JOB, JOB_DONE, JOB_ERROR }; const std::string id; shared_ptr dataModel; Aya::Time expirationTime; int category; double cores; #if !defined(__linux) && !defined(__APPLE__) ATL::CEvent jobCheckLeaseEvent; #endif Aya::signals::connection notifyAliveConnection; JobItemRunStatus status; std::string errorMessage; JobItem(const char* id) : id(id) #if !defined(__linux) && !defined(__APPLE__) , jobCheckLeaseEvent(TRUE, FALSE) #endif , status(RUNNING_JOB) { } void touch(double seconds); double secondsToTimeout() const; }; typedef std::map> JobMap; JobMap jobs; boost::mutex sync; long dataModelCount; boost::mutex currentlyClosingMutex; public: CWebService(bool crashUploadOnly); ~CWebService(); private: void collectPerfData(); void LoadAppSettings(); void LoadClientSettings(RCCServiceSettings& dest); void LoadClientSettings(std::string& clientDest, std::string& thumbnailDest); std::string GetSettingsKey(); std::vector fetchAllowedSecurityVersions(); public: static boost::scoped_ptr singleton; JobMap::iterator getJob(const std::string& jobID) { JobMap::iterator iter = jobs.find(jobID); if (iter == jobs.end()) throw Aya::runtime_error("JobItem %s not found", jobID.c_str()); return iter; } void validateSecurityData() { #if defined(__linux) || defined(__APPLE__) std::mutex mtx; std::condition_variable cv; std::unique_lock lock(mtx); while (cv.wait_for(lock, std::chrono::milliseconds(static_cast(rccSettings.GetValueSecurityDataTimer() * 1000.0))) == std::cv_status::timeout) #else while (::WaitForSingleObject(doneEvent.m_h, rccSettings.GetValueSecurityDataTimer() * 1000) == WAIT_TIMEOUT) #endif { if (DFFlag::UseNewSecurityKeyApi) { if (securityKeyUpdater) securityKeyUpdater->run(); } else { std::string prevVersionData = securityVersionData; std::vector versions = fetchAllowedSecurityVersions(); // security versions changed, update all jobs with new versions if (prevVersionData != securityVersionData) Aya::Network::setSecurityVersions(versions); } if (DFFlag::UseNewMemHashApi) { if (memHashUpdater) { if (DFString::MemHashConfig.size() < 3) { memHashUpdater->run(); } else { Aya::Network::MemHashConfigs hashConfigs; MemHashUpdater::populateMemHashConfigs(hashConfigs, DFString::MemHashConfig); Aya::Network::Players::setGoldMemHashes(hashConfigs); } } } if (md5Updater) md5Updater->run(); } } void validateClientSettings() { #if defined(__linux) || defined(__APPLE__) std::mutex mtx; std::condition_variable cv; std::unique_lock lock(mtx); while (cv.wait_for(lock, std::chrono::milliseconds(static_cast(rccSettings.GetValueClientSettingsTimer() * 1000.0))) == std::cv_status::timeout) #else while (::WaitForSingleObject(doneEvent.m_h, rccSettings.GetValueClientSettingsTimer() * 1000) == WAIT_TIMEOUT) #endif { std::string prevClientSettings = clientSettingsData; std::string prevThumbnailSettings = thumbnailSettingsData; LoadClientSettings(clientSettingsData, thumbnailSettingsData); bool clientChanged = (prevClientSettings != clientSettingsData); bool thumbnailChanged = isThumbnailer && (prevThumbnailSettings != thumbnailSettingsData); if (clientChanged || thumbnailChanged) { // collect all dynamic settings rccDynamicSettings.ReadFromStream(clientSettingsData.c_str()); if (isThumbnailer) { rccDynamicSettings.ReadFromStream(thumbnailSettingsData.c_str()); } // submit datamodel write task to update all dynamic settings boost::mutex::scoped_lock lock(sync); if (jobs.size() > 0) { // HACK: Client settings are global, meaning all datamodels use the same set, // current we only have 1 datamodel running per rccservice, so submit write task just on first datamodel shared_ptr job = jobs.begin()->second; job->dataModel->submitTask( boost::bind(&RCCServiceDynamicSettings::UpdateSettings, &rccDynamicSettings), Aya::DataModelJob::Write); } else rccDynamicSettings.UpdateSettings(); } } } void doCheckLease(boost::shared_ptr job) { try { #if defined(__linux) || defined(__APPLE__) std::mutex mtx; std::condition_variable cv; #endif while (true) { #if defined(__linux) || defined(__APPLE__) std::unique_lock lock(mtx); #endif double sleepTimeInSeconds = job->secondsToTimeout(); if (sleepTimeInSeconds <= 0) { closeJob(job->id); return; } #if !defined(__linux) && !defined(__APPLE__) else if (::WaitForSingleObject(job->jobCheckLeaseEvent.m_h, (DWORD)(sleepTimeInSeconds * 1000.0) + 3) != WAIT_TIMEOUT) return; #else else if (cv.wait_for(lock, std::chrono::milliseconds(static_cast(sleepTimeInSeconds * 1000.0) + 3)) != std::cv_status::timeout) return; #endif } } catch (std::exception& e) { assert(false); Aya::StandardOut::singleton()->printf(Aya::MESSAGE_ERROR, "doCheckLease failure: %s", e.what()); } } void renewLease(const std::string& id, double expirationInSeconds) { boost::mutex::scoped_lock lock(sync); getJob(id)->second->touch(expirationInSeconds); } static void convertToLua(const Aya::Reflection::Variant& source, ns1__LuaValue& dest, soap* soap) { assert(dest.type == ns1__LuaType__LUA_USCORETNIL); assert(dest.value == NULL); if (source.isType()) { dest.type = ns1__LuaType__LUA_USCORETNIL; return; } if (source.isType()) { dest.type = ns1__LuaType__LUA_USCORETBOOLEAN; dest.value = soap_new_std__string(soap, -1); *dest.value = source.get(); return; } if (source.isNumber()) { dest.type = ns1__LuaType__LUA_USCORETNUMBER; dest.value = soap_new_std__string(soap, -1); *dest.value = source.get(); return; } if (source.isType()) { dest.type = ns1__LuaType__LUA_USCORETSTRING; dest.value = soap_new_std__string(soap, -1); *dest.value = source.get(); return; } if (source.isType()) { dest.type = ns1__LuaType__LUA_USCORETSTRING; dest.value = soap_new_std__string(soap, -1); *dest.value = source.get(); return; } if (source.isType>()) { shared_ptr collection = source.cast>(); dest.type = ns1__LuaType__LUA_USCORETTABLE; dest.table = soap_new_ns1__ArrayOfLuaValue(soap, -1); dest.table->LuaValue.resize(collection->size()); for (size_t i = 0; i < collection->size(); ++i) { dest.table->LuaValue[i] = soap_new_ns1__LuaValue(soap, -1); convertToLua((*collection)[i], *dest.table->LuaValue[i], soap); } return; } // TODO: enums! dest.type = ns1__LuaType__LUA_USCORETNIL; } static void convert(const ns1__LuaValue* source, Aya::Reflection::Variant& dest) { switch (source->type) { case ns1__LuaType__LUA_USCORETNIL: break; case ns1__LuaType__LUA_USCORETNUMBER: dest = *source->value; dest.convert(); break; case ns1__LuaType__LUA_USCORETBOOLEAN: dest = *source->value; dest.convert(); break; case ns1__LuaType__LUA_USCORETSTRING: dest = *source->value; break; case ns1__LuaType__LUA_USCORETTABLE: { const size_t count = source->table->LuaValue.size(); // Create a collection so that we can populate it shared_ptr table(new Aya::Reflection::ValueArray(count)); for (size_t i = 0; i < count; ++i) convert(source->table->LuaValue[i], (*table)[i]); // Set the value to a ValueArray type dest = shared_ptr(table); } break; } } // Gathers all non-global Instances that are in the heap void arbiterActivityDump(Aya::Reflection::ValueArray& result) { boost::mutex::scoped_lock lock(sync); for (JobMap::iterator iter = jobs.begin(); iter != jobs.end(); ++iter) { shared_ptr dataModel = iter->second->dataModel; shared_ptr tuple(new Aya::Reflection::ValueArray()); tuple->push_back(dataModel->arbiterName()); tuple->push_back(dataModel->getAverageActivity()); result.push_back(shared_ptr(tuple)); } } // Gathers all non-global Instances that are in the heap void leakDump(Aya::Reflection::ValueArray& result) {} // Gathers diagnostic data. Does NOT require locks on datamodel :) const Aya::Reflection::ValueArray* diag(int type, shared_ptr dataModel) { std::auto_ptr tuple(new Aya::Reflection::ValueArray()); /* This is the format of the Diag data: type == 0 DataModel Count in this process PerfCounter data Task Scheduler (obsolete entry) double threadAffinity double numQueuedJobs double numScheduledJobs double numRunningJobs long threadPoolSize double messageRate double messagePumpDutyCycle DataModel Jobs Info Machine configuration Memory Leak Detection type & 1 leak dump type & 2 attempt to allocate 500k. if success, then true else false type & 4 DataModel dutyCycles */ tuple->push_back(dataModelCount); { // preferably we should fix this #if !defined(__linux) && !defined(__APPLE__) shared_ptr perfCounterData(new Aya::Reflection::ValueArray()); perfCounterData->push_back(s_perfCounter->GetProcessCores()); perfCounterData->push_back(s_perfCounter->GetTotalProcessorTime()); perfCounterData->push_back(s_perfCounter->GetProcessorTime()); perfCounterData->push_back(s_perfCounter->GetPrivateBytes()); perfCounterData->push_back(s_perfCounter->GetPrivateWorkingSetBytes()); perfCounterData->push_back(-1); perfCounterData->push_back(-1); perfCounterData->push_back(s_perfCounter->GetElapsedTime()); perfCounterData->push_back(s_perfCounter->GetVirtualBytes()); perfCounterData->push_back(s_perfCounter->GetPageFileBytes()); perfCounterData->push_back(s_perfCounter->GetPageFaultsPerSecond()); tuple->push_back(shared_ptr(perfCounterData)); #endif } { shared_ptr taskSchedulerData(new Aya::Reflection::ValueArray()); taskSchedulerData->push_back(0); // obsolete taskSchedulerData->push_back(Aya::TaskScheduler::singleton().threadAffinity()); taskSchedulerData->push_back(Aya::TaskScheduler::singleton().numSleepingJobs()); taskSchedulerData->push_back(Aya::TaskScheduler::singleton().numWaitingJobs()); taskSchedulerData->push_back(Aya::TaskScheduler::singleton().numRunningJobs()); taskSchedulerData->push_back((long)Aya::TaskScheduler::singleton().threadPoolSize()); taskSchedulerData->push_back(Aya::TaskScheduler::singleton().schedulerRate()); taskSchedulerData->push_back(Aya::TaskScheduler::singleton().getSchedulerDutyCyclePerThread()); tuple->push_back(shared_ptr(taskSchedulerData)); } if (dataModel) tuple->push_back(dataModel->getJobsInfo()); else tuple->push_back(0); { shared_ptr machineData(new Aya::Reflection::ValueArray()); machineData->push_back(0); machineData->push_back(0); machineData->push_back(0); machineData->push_back(0); tuple->push_back(shared_ptr(machineData)); } { // TODO, use Aya::poolAllocationList and Aya::poolAvailablityList to get complete stats shared_ptr memCounters(new Aya::Reflection::ValueArray()); memCounters->push_back(Aya::Diagnostics::Countable::getCount()); memCounters->push_back(Aya::Diagnostics::Countable::getCount()); memCounters->push_back(Aya::Diagnostics::Countable::getCount()); memCounters->push_back(Aya::Diagnostics::Countable::getCount()); memCounters->push_back(Aya::Diagnostics::Countable::getCount()); memCounters->push_back(Aya::Diagnostics::Countable::getCount()); memCounters->push_back(Aya::Diagnostics::Countable::getCount()); memCounters->push_back(Aya::Diagnostics::Countable::getCount()); memCounters->push_back(Aya::Diagnostics::Countable::getCount()); memCounters->push_back(Aya::Diagnostics::Countable::getCount()); #ifdef AYA_ALLOCATOR_COUNTS memCounters->push_back(Aya::Allocator::getCount()); memCounters->push_back(Aya::Allocator::getCount()); memCounters->push_back(Aya::Allocator::getCount()); memCounters->push_back(Aya::Allocator::getCount()); memCounters->push_back(Aya::Allocator::getCount()); memCounters->push_back(Aya::Allocator::getCount()); memCounters->push_back(Aya::Allocator::getCount()); memCounters->push_back(Aya::Allocator::getCount()); memCounters->push_back(Aya::Allocator::getCount()); memCounters->push_back(Aya::Allocator::getCount()); memCounters->push_back(Aya::Allocator::getCount()); #else for (int i = 0; i < 11; ++i) memCounters->push_back(-1); #endif memCounters->push_back((int)ThumbnailGenerator::totalCount); tuple->push_back(shared_ptr(memCounters)); } if (type & 1) { shared_ptr result(new Aya::Reflection::ValueArray()); leakDump(*result); tuple->push_back(shared_ptr(result)); } if (type & 2) { try { { std::vector v1(100000); std::vector v2(100000); std::vector v3(100000); std::vector v4(100000); std::vector v5(100000); } tuple->push_back(true); } catch (...) { tuple->push_back(false); } } if (type & 4) { shared_ptr result(new Aya::Reflection::ValueArray()); arbiterActivityDump(*result); tuple->push_back(shared_ptr(result)); } return tuple.release(); } void execute(const std::string& jobID, ns1__ScriptExecution* script, std::vector* result, soap* soap) { std::string code = *script->script; if (Aya::ContentProvider::isHttpUrl(code)) { Aya::Http http(code); http.get(code); } shared_ptr dataModel; { boost::mutex::scoped_lock lock(sync); JobMap::iterator iter = getJob(jobID); dataModel = iter->second->dataModel; } std::auto_ptr tuple; { const size_t count = script->arguments ? script->arguments->LuaValue.size() : 0; Aya::Reflection::Tuple args(count); for (size_t i = 0; i < count; ++i) convert(script->arguments->LuaValue[i], args.values[i]); Aya::DataModel::LegacyLock lock(dataModel, Aya::DataModelJob::Write); if (dataModel->isClosed()) throw std::runtime_error("The DataModel is closed"); Aya::ScriptContext* scriptContext = Aya::ServiceProvider::create(dataModel.get()); tuple = scriptContext->executeInNewThread( Aya::Security::WebService, Aya::ProtectedString::fromTrustedSource(code), script->name->c_str(), args); } if (tuple.get()) { result->resize(tuple->values.size()); for (size_t i = 0; i < tuple->values.size(); ++i) { (*result)[i] = soap_new_ns1__LuaValue(soap, -1); convertToLua(tuple->values[i], *(*result)[i], soap); } } else { result->resize(0); } } void diag(int type, std::string jobID, std::vector* result, soap* soap) { std::auto_ptr tuple; { shared_ptr dataModel; if (!jobID.empty()) { boost::mutex::scoped_lock lock(sync); JobMap::iterator iter = getJob(jobID); dataModel = iter->second->dataModel; } tuple.reset(diag(type, dataModel)); } result->resize(tuple->size()); for (size_t i = 0; i < tuple->size(); ++i) { (*result)[i] = soap_new_ns1__LuaValue(soap, -1); convertToLua((*tuple)[i], *(*result)[i], soap); } } void contentDataLoaded(shared_ptr& dataModel) { Aya::CSGDictionaryService* dictionaryService = Aya::ServiceProvider::create(dataModel.get()); Aya::NonReplicatedCSGDictionaryService* nrDictionaryService = Aya::ServiceProvider::create(dataModel.get()); dictionaryService->reparentAllChildData(); } void setupServerConnections(Aya::DataModel* dataModel) { if (!dataModel) { return; } } shared_ptr createJob(const ns1__Job& job, bool startHeartbeat, shared_ptr& dataModel) { srand(Aya::randomSeed()); // make sure this thread is seeded std::string id = job.id; dataModel = Aya::DataModel::createDataModel( startHeartbeat, new Aya::NullVerb(NULL, ""), false, Aya::GameBasicSettings::singleton().getVirtualVersion()); Aya::AyaService* ayaService = Aya::ServiceProvider::create(dataModel.get()); ayaService->setInitialVersion(Aya::GameBasicSettings::singleton().getVirtualVersion()); setupServerConnections(dataModel.get()); Aya::Network::Players* players = dataModel->find(); if (players) { LoadClientSettings(rccSettings); if (rccSettings.GetError()) { AYACRASH(rccSettings.GetErrorString().c_str()); } { bool useCurl = rand() % 100 < rccSettings.GetValueHttpUseCurlPercentageRCC(); FASTLOG1(FLog::RCCDataModelInit, "Using CURL = %d", useCurl); Aya::Http::SetUseCurl(useCurl); Aya::Http::SetUseStatistics(true); } FASTLOGS(FLog::RCCDataModelInit, "Creating Data Model, Windows MD5: %s", rccSettings.GetValueWindowsMD5()); FASTLOGS(FLog::RCCDataModelInit, "Creating Data Model, Mac MD5: %s", rccSettings.GetValueMacMD5()); FASTLOGS(FLog::RCCDataModelInit, "Creating Data Model, Windows Player Beta MD5: %s", rccSettings.GetValueWindowsPlayerBetaMD5()); players->setGoldenHashes(rccSettings.GetValueWindowsMD5(), rccSettings.GetValueMacMD5(), rccSettings.GetValueWindowsPlayerBetaMD5()); Aya::DataModel::LegacyLock lock(dataModel, Aya::DataModelJob::Write); dataModel->create(); } dataModel->workspaceLoadedSignal.connect( [this](std::shared_ptr& dataModel) { if (Aya::AdService* adService = dataModel->create()) { adService->sendServerVideoAdVerification.connect(boost::bind(&Aya::AdService::checkCanPlayVideoAd, adService, _1, _2)); adService->sendServerRecordImpression.connect(boost::bind(&Aya::AdService::sendAdImpression, adService, _1, _2, _3)); } Aya::CSGDictionaryService* dictionaryService = Aya::ServiceProvider::create(dataModel.get()); Aya::NonReplicatedCSGDictionaryService* nrDictionaryService = Aya::ServiceProvider::create(dataModel.get()); dictionaryService->reparentAllChildData(); }); dataModel->jobId = id; shared_ptr j(new JobItem(id.c_str())); j->dataModel = dataModel; j->category = job.category; j->cores = job.cores; j->touch(job.expirationInSeconds); { boost::mutex::scoped_lock lock(sync); if (jobs.find(id) != jobs.end()) throw Aya::runtime_error("JobItem %s already exists", id.c_str()); jobs[id] = j; } return j; } void batchJob(const ns1__Job& job, ns1__ScriptExecution* script, std::vector* result, soap* soap) { std::string id = job.id; shared_ptr dataModel; #if !defined(__linux) && !defined(__APPLE__) ::InterlockedIncrement(&dataModelCount); #endif try { shared_ptr j = createJob(job, false, dataModel); FASTLOGS(FLog::RCCServiceJobs, "Opened Batch JobItem %s", id.c_str()); FASTLOG1(FLog::RCCServiceJobs, "DataModel: %p", dataModel.get()); // Spin off a thread for the BatchJob to execute in #if !defined(__linux) && !defined(__APPLE__) boost::thread(Aya::thread_wrapper(boost::bind(&CWebService::asyncExecute, this, id, script, result, soap), "AsyncExecute")); #else // linux - Object destroyed immediately after creation; did you mean to name the object? (fix available)clang-tidybugprone-unused-raii boost::thread give_me_a_name( Aya::thread_wrapper(boost::bind(&CWebService::asyncExecute, this, id, script, result, soap), "AsyncExecute")); std::mutex mtx; std::condition_variable cv; #endif while (true) { #if defined(__linux) || defined(__APPLE__) std::unique_lock lock(mtx); #endif double sleepTimeInSeconds = j->secondsToTimeout(); if (sleepTimeInSeconds <= 0) // This case seems like an edge case (we already ran out of time) { #if !defined(__linux) && !defined(__APPLE__) if (::WaitForSingleObject(j->jobCheckLeaseEvent.m_h, 1) != WAIT_TIMEOUT) { if (j->status == JobItem::JOB_ERROR) throw Aya::runtime_error(j->errorMessage.c_str()); } #endif closeJob(id); throw Aya::runtime_error("BatchJob Timeout"); } #if !defined(__linux) && !defined(__APPLE__) else if (::WaitForSingleObject(j->jobCheckLeaseEvent.m_h, (DWORD)(sleepTimeInSeconds * 1000.0) + 3) == WAIT_TIMEOUT) #else else if (cv.wait_for(lock, std::chrono::milliseconds(static_cast(sleepTimeInSeconds * 1000.0) + 3)) == std::cv_status::timeout) #endif { // do nothing, just continue looping. Probably sleepTimeInSeconds will be negative, and we'll throw } else { // jobCheckLeaseEvent was set if (j->status == JobItem::JOB_ERROR) throw Aya::runtime_error(j->errorMessage.c_str()); // TODO: Shouldn't this be called BEFORE we throw?????? closeJob(id); return; } } } catch (std::exception&) { throw; } } void asyncExecute(const std::string& id, ns1__ScriptExecution* script, std::vector* result, soap* soap) { try { this->execute(id, script, result, soap); closeJob(id); } catch (std::exception& e) { char szMsg[1024]; #if !defined(__linux) && !defined(__APPLE__) StringCchCopy(szMsg, ARRAYSIZE(szMsg), e.what()); #endif closeJob(id, szMsg); } } void openJob(const ns1__Job& job, ns1__ScriptExecution* script, std::vector* result, soap* soap, bool startHeartbeat) { std::string id = job.id; Aya::Http::gameID = job.id; shared_ptr dataModel; #if !defined(__linux) && !defined(__APPLE__) ::InterlockedIncrement(&dataModelCount); #endif try { shared_ptr j = createJob(job, startHeartbeat, dataModel); try { Aya::DataModel* pDataModel = dataModel.get(); pDataModel->create()->setBaseUrl(GetBaseURL()); // Monitor the job and close it if needed boost::thread(Aya::thread_wrapper(boost::bind(&CWebService::doCheckLease, this, j), "Check Expiration")); FASTLOGS(FLog::RCCServiceJobs, "Opened JobItem %s", id.c_str()); FASTLOG1(FLog::RCCServiceJobs, "DataModel: %p", pDataModel); this->execute(id, script, result, soap); Aya::RunService* runService = Aya::ServiceProvider::find(pDataModel); AYAASSERT(runService != NULL); j->notifyAliveConnection = runService->heartbeatSignal.connect(boost::bind(&CWebService::notifyAlive, this, _1)); { Aya::DataModel::LegacyLock lock(pDataModel, Aya::DataModelJob::Write); pDataModel->loadCoreScripts(); } } catch (std::exception& e) { char szMsg[1024]; #if !defined(__linux) && !defined(__APPLE__) StringCchCopy(szMsg, ARRAYSIZE(szMsg), e.what()); #endif boost::mutex::scoped_lock lock(sync); jobs.erase(id); throw; } } catch (std::exception& e) { char szMsg[1024]; #if !defined(__linux) && !defined(__APPLE__) StringCchCopy(szMsg, ARRAYSIZE(szMsg), e.what()); #endif FASTLOGS(FLog::RCCServiceJobs, "Closing DataModel due to exception: %s", szMsg); FASTLOG1(FLog::RCCServiceJobs, "DataModel: %p", dataModel.get()); closeDataModel(dataModel); dataModel.reset(); // ::InterlockedDecrement(&dataModelCount); throw; } } void closeDataModel(shared_ptr dataModel) { Aya::Security::Impersonator impersonate(Aya::Security::WebService); // CrashAfterTimeout crash(90); // If after 90 seconds the datamodel doesn't close, then CRASH!!!! Aya::DataModel::closeDataModel(dataModel); } void notifyAlive(const Aya::Heartbeat& h) { #if !defined(__linux) && !defined(__APPLE__) mainLogManager->NotifyFGThreadAlive(); #endif } void closeJob(const std::string& jobID, const char* errorMessage = NULL) { // Take a mutex for the duration of closeJob here. The arbiter thinks that as // soon as closeJob returns it is safe to force kill the rcc process, so make // sure that if it is in the process of closing the datamodel any parallel requests // to close the data model block at the mutex until the first one finishes. boost::mutex::scoped_lock closeLock(currentlyClosingMutex); shared_ptr dataModel; { boost::mutex::scoped_lock lock(sync); JobMap::iterator iter = jobs.find(jobID); if (iter != jobs.end()) { if (errorMessage) { iter->second->errorMessage = errorMessage; iter->second->status = JobItem::JOB_ERROR; } else { iter->second->status = JobItem::JOB_DONE; } // d9mz - I hope this line isn't important #if !defined(__linux) && !defined(__APPLE__) iter->second->jobCheckLeaseEvent.Set(); #endif dataModel = iter->second->dataModel; iter->second->notifyAliveConnection.disconnect(); jobs.erase(iter); } } if (dataModel) { FASTLOGS(FLog::RCCServiceJobs, "Closing JobItem %s", jobID.c_str()); FASTLOG1(FLog::RCCServiceJobs, "DataModel: %p", dataModel.get()); closeDataModel(dataModel); dataModel.reset(); #if !defined(__linux) && !defined(__APPLE__) if (::InterlockedDecrement(&dataModelCount) == 0) mainLogManager->DisableHangReporting(); #endif } } int jobCount() { return jobs.size(); } void getExpiration(const std::string& jobID, double* timeout) { boost::mutex::scoped_lock lock(sync); *timeout = getJob(jobID)->second->secondsToTimeout(); } void closeExpiredJobs(int* result) { std::vector jobsToClose; { boost::mutex::scoped_lock lock(sync); JobMap::iterator end = jobs.end(); for (JobMap::iterator iter = jobs.begin(); iter != end; ++iter) if (iter->second->secondsToTimeout() <= 0) jobsToClose.push_back(iter->first); } *result = jobsToClose.size(); std::for_each(jobsToClose.begin(), jobsToClose.end(), boost::bind(&CWebService::closeJob, this, _1, (const char*)NULL)); } void closeAllJobs(int* result) { FASTLOG(FLog::RCCServiceJobs, "Closing all jobs command"); std::vector jobsToClose; { boost::mutex::scoped_lock lock(sync); JobMap::iterator end = jobs.end(); for (JobMap::iterator iter = jobs.begin(); iter != end; ++iter) jobsToClose.push_back(iter->first); } *result = jobsToClose.size(); std::for_each(jobsToClose.begin(), jobsToClose.end(), boost::bind(&CWebService::closeJob, this, _1, (const char*)NULL)); } void getAllJobs(std::vector& result, soap* soap) { boost::mutex::scoped_lock lock(sync); result.resize(jobs.size()); int i = 0; JobMap::iterator iter = jobs.begin(); JobMap::iterator end = jobs.end(); while (iter != end) { ns1__Job* job = soap_new_ns1__Job(soap, -1); job->expirationInSeconds = iter->second->secondsToTimeout(); job->category = iter->second->category; job->cores = iter->second->cores; job->id = iter->first; result[i] = job; ++iter; ++i; } } }; boost::scoped_ptr CWebService::singleton; void stop_CWebService() { CWebService::singleton.reset(); } void start_CWebService() { CWebService::singleton.reset(new CWebService(false)); } AYA_REGISTER_CLASS(ThumbnailGenerator); void CWebService::collectPerfData() { #if !defined(__linux) && !defined(__APPLE__) while (::WaitForSingleObject(doneEvent.m_h, 1000) == WAIT_TIMEOUT) // used as an interruptible sleep. s_perfCounter->CollectData(); #endif } CWebService::CWebService(bool crashUploadOnly) #if !defined(__linux) && !defined(__APPLE__) : doneEvent(TRUE, FALSE) , dataModelCount(0) #else : dataModelCount(0) #endif { { #if !defined(__linux) && !defined(__APPLE__) CVersionInfo vi; vi.Load(_AtlBaseModule.m_hInst); Aya::DebugSettings::robloxVersion = vi.GetFileVersionAsDotString(); Aya::Analytics::setReporter("RCCService"); Aya::Analytics::setAppVersion(vi.GetFileVersionAsString()); #else Aya::DebugSettings::robloxVersion = "69.420.00.00"; // Funny Aya::Analytics::setReporter("RCCService"); Aya::Analytics::setAppVersion("69.420.00.00"); #endif } #if !defined(__linux) && !defined(__APPLE__) s_perfCounter = CProcessPerfCounter::getInstance(); #endif perfData.reset(new boost::thread(Aya::thread_wrapper(boost::bind(&CWebService::collectPerfData, this), "CWebService::collectPerfData"))); Aya::Http::init(Aya::Http::WinHttp, Aya::Http::CookieSharingSingleProcessMultipleThreads); Aya::Http::requester = "Server"; Aya::Profiling::init(false); static Aya::FactoryRegistrator registerFactoryObjects; // this needs to be here so srand is called before rand #if !defined(__linux) && !defined(__APPLE__) RobloxCrashReporter::silent = true; mainLogManager->WriteCrashDump(); #endif isThumbnailer = false; LoadAppSettings(); LoadClientSettings(rccSettings); if (rccSettings.GetError()) { AYACRASH(rccSettings.GetErrorString().c_str()); } Aya::TaskSchedulerSettings::singleton(); Aya::TaskScheduler::singleton().setThreadCount(Aya::TaskScheduler::ThreadPoolConfig(FInt::RCCServiceThreadCount)); // Force loading of settings classes Aya::GameSettings::singleton(); Aya::GameBasicSettings::singleton(); Aya::LuaSettings::singleton(); Aya::DebugSettings::singleton(); Aya::PhysicsSettings::singleton(); Aya::Soundscape::SoundService::soundDisabled = true; // Initialize the network code Aya::Network::initWithServerSecurity(); // If crashUploadOnly = true, don't create a separate thread of control for uploading #if !defined(__linux) && !defined(__APPLE__) static DumpErrorUploader dumpErrorUploader(!crashUploadOnly, "RCCService"); // KISEKI TODO: This shit hangs very easily bc its slow as fuck.. std::string dmpHandlerUrl = GetGridUrl(::GetBaseURL(), FFlag::UseDataDomain); dumpErrorUploader.InitCrashEvent(dmpHandlerUrl, mainLogManager->getCrashEventName()); dumpErrorUploader.Upload(dmpHandlerUrl); #endif fetchClientSettingsThread.reset( new boost::thread(Aya::thread_wrapper(boost::bind(&CWebService::validateClientSettings, this), "CWebService::validateClientSettings"))); // load and set security versions immediately at start up so it's guaranteed to be there when server is launched /* securityKeyUpdater.reset(new SecurityKeyUpdater(GetSecurityKeyUrl2(GetBaseURL(), "2b4ba7fc-5843-44cf-b107-ba22d3319dcd"))); md5Updater.reset(new MD5Updater(GetMD5HashUrl(GetBaseURL(), "2b4ba7fc-5843-44cf-b107-ba22d3319dcd"))); memHashUpdater.reset(new MemHashUpdater(GetMemHashUrl(GetBaseURL(), "2b4ba7fc-5843-44cf-b107-ba22d3319dcd"))); if (DFFlag::UseNewSecurityKeyApi) { securityKeyUpdater->run(); } else { std::vector versions = fetchAllowedSecurityVersions(); Aya::Network::setSecurityVersions(versions); } md5Updater->run(); if (DFFlag::UseNewMemHashApi) { if (DFString::MemHashConfig.size() < 3) { memHashUpdater->run(); } else { Aya::Network::MemHashConfigs hashConfigs; MemHashUpdater::populateMemHashConfigs(hashConfigs, DFString::MemHashConfig); Aya::Network::Players::setGoldMemHashes(hashConfigs); } } // this thread uses client setting values, so it must be started AFTER client settings are loaded // create a thread to periodically check for security key changes fetchSecurityDataThread.reset(new boost::thread(Aya::thread_wrapper(boost::bind(&CWebService::validateSecurityData, this), "CWebService::validateSecurityData"))); counters.reset(new CountersClient(GetBaseURL().c_str(), "76E5A40C-3AE1-4028-9F10-7C62520BD94F", NULL)); */ Aya::ViewBase::InitPluginModules(); } CWebService::~CWebService() { // i hope this line isn't important (again) #if !defined(__linux) && !defined(__APPLE__) doneEvent.Set(); #endif perfData->join(); // fetchSecurityDataThread->join(); // fetchClientSettingsThread->join(); // TODO: this line crashes sometimes :-( int result; closeAllJobs(&result); Aya::ViewBase::ShutdownPluginModules(); } std::string CWebService::GetSettingsKey() { return "RccThumbnailers"; } void CWebService::LoadClientSettings(RCCServiceSettings& dest) { // cache settings in a string before processing LoadClientSettings(clientSettingsData, thumbnailSettingsData); LoadClientSettingsFromString("RccThumbnailers", clientSettingsData, &dest); } void CWebService::LoadClientSettings(std::string& clientDest, std::string& thumbnailDest) { clientDest.clear(); thumbnailDest.clear(); std::string key = GetSettingsKey(); FetchClientSettingsData("RccThumbnailers", "D6925E56-BFB9-4908-AAA2-A5B1EC4B2D79", &thumbnailDest); FetchClientSettingsData("RccThumbnailers", "D6925E56-BFB9-4908-AAA2-A5B1EC4B2D79", &clientDest); bool invalidClientSettings = (clientDest.empty() || key.length() == 0); bool invalidThumbnailerSettings = (thumbnailDest.empty() && isThumbnailer); if (invalidClientSettings || invalidThumbnailerSettings) { Aya::StandardOut::singleton()->printf( Aya::MESSAGE_ERROR, "Unable to load ClientSettings / ThumbnailerSettings! Please check the webserver. Unexpected behavior may occur."); Aya::StandardOut::singleton()->printf(Aya::MESSAGE_ERROR, "-- invalidClientSettings: %s, invalidThumbnailerSettings: %s", invalidClientSettings ? "true" : "false", invalidThumbnailerSettings ? "true" : "false"); Aya::StandardOut::singleton()->printf(Aya::MESSAGE_ERROR, "-- clientDest.empty(): %s, key.length() == 0: %s", clientDest.empty() ? "true" : "false", key.length() == 0 ? "true" : "false"); } } void CWebService::LoadAppSettings() { SetBaseURL(GetBaseURL()); } std::vector CWebService::fetchAllowedSecurityVersions() { const std::string& baseUrl = GetBaseURL(); if (baseUrl.size() == 0) AYACRASH(); // y u no set BaseURL?? std::vector versions; std::string url = GetSecurityKeyUrl(baseUrl, "2b4ba7fc-5843-44cf-b107-ba22d3319dcd"); Aya::Http request(url); std::string allowedSecurityVerionsData = ""; try { request.get(allowedSecurityVerionsData); // no need to continue if security version did not change if (allowedSecurityVerionsData == securityVersionData) return std::vector(); // parse json data rapidjson::Document document; if (!document.Parse(allowedSecurityVerionsData.c_str()).HasParseError()) { if (document.HasMember("data") && document["data"].IsArray()) { const rapidjson::Value& dataArray = document["data"]; for (rapidjson::SizeType i = 0; i < dataArray.Size(); i++) { // version = data + salt #ifndef __linux std::string version = Aya::sha1(dataArray[i].GetString() + std::string("askljfLUZF")); #else // this causes a linker error, im too lazy to go edit cmakelists std::string version = (dataArray[i].GetString() + std::string("askljfLUZF")); #endif versions.push_back(version); } } } securityVersionData = allowedSecurityVerionsData; } catch (std::exception ex) { FASTLOG(FLog::Always, "LoadAllowedPlayerVersions exception"); } return versions; } void CWebService::JobItem::touch(double seconds) { expirationTime = Aya::Time::now() + Aya::Time::Interval(seconds); } double CWebService::JobItem::secondsToTimeout() const { return (expirationTime - Aya::Time::now()).seconds(); } int RCCServiceSoapService::HelloWorld(_ns1__HelloWorld* ns1__HelloWorld, _ns1__HelloWorldResponse& ns1__HelloWorldResponse) { BEGIN_PRINT(HelloWorld, "HelloWorld"); ns1__HelloWorldResponse.HelloWorldResult = soap_new_std__string(this, -1); *ns1__HelloWorldResponse.HelloWorldResult = "Hello World"; END_PRINT(HelloWorld, "HelloWorld"); return 0; } int RCCServiceSoapService::Diag(_ns1__Diag* ns1__Diag, _ns1__DiagResponse& ns1__DiagResponse) { BEGIN_PRINT(Diag, "Diag"); Aya::Security::Impersonator impersonate(Aya::Security::WebService); CWebService::singleton->diag(ns1__Diag->type, *ns1__Diag->jobID, &ns1__DiagResponse.DiagResult, this); END_PRINT(Diag, "Diag"); return 0; } int RCCServiceSoapService::DiagEx(_ns1__DiagEx* ns1__DiagEx, _ns1__DiagExResponse& ns1__DiagExResponse) { BEGIN_PRINT(DiagEx, "DiagEx"); Aya::Security::Impersonator impersonate(Aya::Security::WebService); ns1__DiagExResponse.DiagExResult = soap_new_ns1__ArrayOfLuaValue(this, -1); CWebService::singleton->diag(ns1__DiagEx->type, *ns1__DiagEx->jobID, &ns1__DiagExResponse.DiagExResult->LuaValue, this); END_PRINT(DiagEx, "DiagEx"); return 0; } int RCCServiceSoapService::GetVersion(_ns1__GetVersion* ns1__GetVersion, _ns1__GetVersionResponse& ns1__GetVersionResponse) { BEGIN_PRINT(GetVersion, "GetVersion"); ns1__GetVersionResponse.GetVersionResult = Aya::DebugSettings::robloxVersion.c_str(); END_PRINT(GetVersion, "GetVersion"); return 0; } int RCCServiceSoapService::GetStatus(_ns1__GetStatus* ns1__GetStatus, _ns1__GetStatusResponse& ns1__GetStatusResponse) { BEGIN_PRINT(GetStatus, "GetStatus"); ns1__GetStatusResponse.GetStatusResult = soap_new_ns1__Status(this, -1); ns1__GetStatusResponse.GetStatusResult->version = soap_new_std__string(this, -1); *ns1__GetStatusResponse.GetStatusResult->version = Aya::DebugSettings::robloxVersion.c_str(); ns1__GetStatusResponse.GetStatusResult->environmentCount = CWebService::singleton->jobCount(); END_PRINT(GetStatus, "GetStatus"); return 0; } int RCCServiceSoapService::OpenJob(_ns1__OpenJob* ns1__OpenJob, _ns1__OpenJobResponse& ns1__OpenJobResponse) { BEGIN_PRINT(OpenJob, "OpenJob"); Aya::Security::Impersonator impersonate(Aya::Security::WebService); CWebService::singleton->openJob(*ns1__OpenJob->job, ns1__OpenJob->script, &ns1__OpenJobResponse.OpenJobResult, this, true); END_PRINT(OpenJob, "OpenJob"); return 0; } int RCCServiceSoapService::OpenJobEx(_ns1__OpenJobEx* ns1__OpenJobEx, _ns1__OpenJobExResponse& ns1__OpenJobExResponse) { BEGIN_PRINT(OpenJobEx, "OpenJobEx"); Aya::Security::Impersonator impersonate(Aya::Security::WebService); ns1__OpenJobExResponse.OpenJobExResult = soap_new_ns1__ArrayOfLuaValue(this, -1); CWebService::singleton->openJob(*ns1__OpenJobEx->job, ns1__OpenJobEx->script, &ns1__OpenJobExResponse.OpenJobExResult->LuaValue, this, true); END_PRINT(OpenJobEx, "OpenJobEx"); return 0; } int RCCServiceSoapService::Execute(_ns1__Execute* ns1__Execute, _ns1__ExecuteResponse& ns1__ExecuteResponse) { BEGIN_PRINT(Execute, "Execute"); try { Aya::Security::Impersonator impersonate(Aya::Security::WebService); CWebService::singleton->execute(ns1__Execute->jobID, ns1__Execute->script, &ns1__ExecuteResponse.ExecuteResult, this); } catch (std::exception&) { throw; } END_PRINT(Execute, "Execute"); return 0; } int RCCServiceSoapService::ExecuteEx(_ns1__ExecuteEx* ns1__ExecuteEx, _ns1__ExecuteExResponse& ns1__ExecuteExResponse) { BEGIN_PRINT(ExecuteEx, "ExecuteEx"); try { Aya::Security::Impersonator impersonate(Aya::Security::WebService); ns1__ExecuteExResponse.ExecuteExResult = soap_new_ns1__ArrayOfLuaValue(this, -1); CWebService::singleton->execute(ns1__ExecuteEx->jobID, ns1__ExecuteEx->script, &ns1__ExecuteExResponse.ExecuteExResult->LuaValue, this); } catch (std::exception&) { throw; } END_PRINT(ExecuteEx, "ExecuteEx"); return 0; } int RCCServiceSoapService::CloseJob(_ns1__CloseJob* ns1__CloseJob, _ns1__CloseJobResponse& ns1__CloseJobResponse) { BEGIN_PRINT(CloseJob, "CloseJob"); try { Aya::Security::Impersonator impersonate(Aya::Security::WebService); CWebService::singleton->closeJob(ns1__CloseJob->jobID); } catch (std::exception&) { throw; } END_PRINT(CloseJob, "CloseJob"); return 0; } int RCCServiceSoapService::RenewLease(_ns1__RenewLease* ns1__RenewLease, _ns1__RenewLeaseResponse& ns1__RenewLeaseResponse) { BEGIN_PRINT(RenewLease, "RenewLease"); try { Aya::Security::Impersonator impersonate(Aya::Security::WebService); CWebService::singleton->renewLease(ns1__RenewLease->jobID, ns1__RenewLease->expirationInSeconds); } catch (std::exception&) { throw; } END_PRINT(RenewLease, "RenewLease"); return 0; } int RCCServiceSoapService::BatchJob(_ns1__BatchJob* ns1__BatchJob, _ns1__BatchJobResponse& ns1__BatchJobResponse) { BEGIN_PRINT(BatchJob, "BatchJob"); Aya::Security::Impersonator impersonate(Aya::Security::WebService); // Batch jobs are completed synchronously, so there is no need to start the heartbeat try { CWebService::singleton->batchJob(*ns1__BatchJob->job, ns1__BatchJob->script, &ns1__BatchJobResponse.BatchJobResult, this); } catch (std::exception&) { throw; } END_PRINT(BatchJob, "BatchJob"); return 0; } int RCCServiceSoapService::BatchJobEx(_ns1__BatchJobEx* ns1__BatchJobEx, _ns1__BatchJobExResponse& ns1__BatchJobExResponse) { BEGIN_PRINT(BatchJobEx, "BatchJobEx"); Aya::Security::Impersonator impersonate(Aya::Security::WebService); ns1__BatchJobExResponse.BatchJobExResult = soap_new_ns1__ArrayOfLuaValue(this, -1); // Batch jobs are completed synchronously, so there is no need to start the heartbeat try { CWebService::singleton->batchJob(*ns1__BatchJobEx->job, ns1__BatchJobEx->script, &ns1__BatchJobExResponse.BatchJobExResult->LuaValue, this); } catch (std::exception&) { throw; } END_PRINT(BatchJobEx, "BatchJobEx"); return 0; } int RCCServiceSoapService::GetExpiration(_ns1__GetExpiration* ns1__GetExpiration, _ns1__GetExpirationResponse& ns1__GetExpirationResponse) { BEGIN_PRINT(GetExpiration, "GetExpiration"); try { CWebService::singleton->getExpiration(ns1__GetExpiration->jobID, &ns1__GetExpirationResponse.GetExpirationResult); } catch (std::exception&) { #if !defined(__linux) && !defined(__APPLE__) ::InterlockedDecrement(&getExpirationCount); #endif throw; } END_PRINT(GetExpiration, "GetExpiration"); return 0; } int RCCServiceSoapService::GetAllJobs(_ns1__GetAllJobs* ns1__GetAllJobs, _ns1__GetAllJobsResponse& ns1__GetAllJobsResponse) { BEGIN_PRINT(GetAllJobs, "GetAllJobs"); Aya::Security::Impersonator impersonate(Aya::Security::WebService); CWebService::singleton->getAllJobs(ns1__GetAllJobsResponse.GetAllJobsResult, this); END_PRINT(GetAllJobs, "GetAllJobs"); return 0; } int RCCServiceSoapService::GetAllJobsEx(_ns1__GetAllJobsEx* ns1__GetAllJobsEx, _ns1__GetAllJobsExResponse& ns1__GetAllJobsExResponse) { BEGIN_PRINT(GetAllJobsEx, "GetAllJobsEx"); Aya::Security::Impersonator impersonate(Aya::Security::WebService); ns1__GetAllJobsExResponse.GetAllJobsExResult = soap_new_ns1__ArrayOfJob(this, -1); CWebService::singleton->getAllJobs(ns1__GetAllJobsExResponse.GetAllJobsExResult->Job, this); END_PRINT(GetAllJobsEx, "GetAllJobsEx"); return 0; } int RCCServiceSoapService::CloseExpiredJobs( _ns1__CloseExpiredJobs* ns1__CloseExpiredJobs, _ns1__CloseExpiredJobsResponse& ns1__CloseExpiredJobsResponse) { BEGIN_PRINT(GetAllJobs, "CloseExpiredJobs"); Aya::Security::Impersonator impersonate(Aya::Security::WebService); CWebService::singleton->closeExpiredJobs(&ns1__CloseExpiredJobsResponse.CloseExpiredJobsResult); END_PRINT(GetAllJobs, "CloseExpiredJobs"); return 0; } int RCCServiceSoapService::CloseAllJobs(_ns1__CloseAllJobs* ns1__CloseAllJobs, _ns1__CloseAllJobsResponse& ns1__CloseAllJobsResponse) { BEGIN_PRINT(GetAllJobs, "CloseAllJobs"); Aya::Security::Impersonator impersonate(Aya::Security::WebService); CWebService::singleton->closeAllJobs(&ns1__CloseAllJobsResponse.CloseAllJobsResult); END_PRINT(GetAllJobs, "CloseAllJobs"); return 0; }