Advertisement
Guest User

Untitled

a guest
Dec 8th, 2018
1,900
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 143.22 KB | None | 0 0
  1. /* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
  2. #include "stdafx.h"
  3.  
  4. #include "V8DataModel/DataModel.h" // TODO - minimize these includes, and in the .h file
  5. #include "V8DataModel/DebugSettings.h"
  6. #include "V8DataModel/GameBasicSettings.h"
  7. #include "V8DataModel/Workspace.h"
  8. #include "V8DataModel/ChangeHistory.h"
  9. #include "V8DataModel/Camera.h"
  10. #include "V8DataModel/UserController.h"
  11. #include "V8DataModel/KeyframeSequenceProvider.h"
  12. #include "V8DataModel/Hopper.h"
  13. #include "V8DataModel/Backpack.h"
  14. #include "V8DataModel/Tool.h"
  15. #include "V8DataModel/MouseCommand.h"
  16. #include "V8DataModel/Teams.h"
  17. #include "V8DataModel/Lighting.h"
  18. #include "V8DataModel/Accoutrement.h"
  19. #include "V8DataModel/ContentProvider.h"
  20. #include "V8DataModel/ContextActionService.h"
  21. #include "V8DataModel/StarterPlayerService.h"
  22. #include "V8DataModel/ChangeHistory.h"
  23. #include "V8DataModel/SpawnLocation.h"
  24. #include "V8DataModel/Visit.h"
  25. #include "V8DataModel/PlayerGui.h"
  26. #include "V8DataModel/TextButton.h"
  27. #include "V8DataModel/Test.h"
  28. #include "V8DataModel/MegaCluster.h"
  29. #include "V8datamodel/Stats.h"
  30. #include "V8datamodel/PartInstance.h"
  31.  
  32. #include "V8DataModel/BadgeService.h"
  33. #include "V8DataModel/ChatService.h"
  34. #include "V8DataModel/CollectionService.h"
  35. #include "V8DataModel/CookiesEngineService.h"
  36. #include "V8DataModel/DebrisService.h"
  37. #include "V8DataModel/FriendService.h"
  38. #include "V8DataModel/GamePassService.h"
  39. #include "V8DataModel/GeometryService.h"
  40. #include "V8DataModel/GuiService.h"
  41. #include "V8DataModel/ImageButton.h"
  42. #include "V8DataModel/InsertService.h"
  43. #include "V8DataModel/JointsService.h"
  44. #include "V8DataModel/MarketplaceService.h"
  45. #include "V8DataModel/PointsService.h"
  46. #include "V8DataModel/PersonalServerService.h"
  47. #include "V8DataModel/PhysicsService.h"
  48. #include "V8DataModel/RenderHooksService.h"
  49. #include "V8DataModel/ScriptService.h"
  50. #include "V8DataModel/SocialService.h"
  51. #include "V8DataModel/TeleportService.h"
  52. #include "V8DataModel/UserInputService.h"
  53. #include "V8DataModel/HackDefines.h"
  54. #include "v8datamodel/ReplicatedStorage.h"
  55. #include "v8datamodel/RobloxReplicatedStorage.h"
  56. #include "v8datamodel/ReplicatedFirst.h"
  57. #include "v8datamodel/ServerScriptService.h"
  58. #include "v8datamodel/ServerStorage.h"
  59. #include "v8datamodel/AssetService.h"
  60. #include "v8datamodel/DataStoreService.h"
  61. #include "v8datamodel/HttpService.h"
  62. #include "V8DataModel/LogService.h"
  63. #include "V8DataModel/AdService.h"
  64. #include "V8DataModel/NotificationService.h"
  65. #include "V8DataModel/CSGDictionaryService.h"
  66. #include "V8DataModel/NonReplicatedCSGDictionaryService.h"
  67. #include "V8DataModel/ScreenGui.h"
  68. #include "v8datamodel/HttpRbxApiService.h"
  69. #include "v8datamodel/GamepadService.h"
  70. #include "v8datamodel/TextBox.h"
  71.  
  72. #include "v8datamodel/Light.h"
  73.  
  74. #include "Humanoid/Humanoid.h"
  75.  
  76. #include "Network/Api.h"
  77. #include "Network/Player.h"
  78. #include "Network/Players.h"
  79. #include "Network/NetworkOwner.h"
  80. #include "Network/GameConfigurer.h"
  81.  
  82. #include "V8World/Contact.h"
  83. #include "V8Kernel/ContactConnector.h"
  84. #include "V8Kernel/Kernel.h"
  85. #include "V8World/ContactManager.h" // for fastclear
  86.  
  87. #include "v8xml/WebParser.h"
  88.  
  89. #include "Script/luainstancebridge.h"
  90. #include "Script/scriptcontext.h"
  91. #include "Script/Script.h"
  92. #include "Script/CoreScript.h"
  93.  
  94. #include "Util/Statistics.h"
  95. #include "Util/ScriptInformationProvider.h"
  96. #include "Util/SimSendFilter.h"
  97. #include "Util/SoundWorld.h"
  98. #include "Util/SoundService.h"
  99. #include "Util/StandardOut.h"
  100. #include "Util/UserInputBase.h"
  101. #include "Util/http.h"
  102. #include "Util/profiling.h"
  103. #include "Util/IMetric.h"
  104. #include "Util/Statistics.h"
  105. #include "Util/ContentFilter.h"
  106. #include "Util/RobloxGoogleAnalytics.h"
  107.  
  108. #include "rbx/Log.h"
  109. #include "FastLog.h"
  110. #include "rbx/threadsafe.h"
  111. #include "rbx/Countable.h"
  112.  
  113. #include "AppDraw/DrawPrimitives.h"
  114.  
  115. #include "g3d/format.h"
  116.  
  117. #include "RbxG3D/RbxTime.h"
  118.  
  119. #include "V8Xml/Serializer.h"
  120. #include "V8Xml/XmlSerializer.h"
  121. #include "boost/cast.hpp"
  122. #include "boost/scoped_ptr.hpp"
  123.  
  124. #include "GfxBase/IAdornableCollector.h"
  125. #include <fstream>
  126. #include "rbx/RbxDbgInfo.h"
  127. #include <boost/format.hpp>
  128.  
  129. #include "util/RbxStringTable.h"
  130. #include "format_string.h"
  131. #include "VMProtectSDK.h"
  132.  
  133. #include "RbxFormat.h"
  134.  
  135. #include "util/MemoryStats.h"
  136.  
  137. #include "../NetworkSettings.h"
  138.  
  139. #include "boost/algorithm/string/split.hpp"
  140. #include "boost/algorithm/string/classification.hpp"
  141.  
  142. #include "rbx/Profiler.h"
  143.  
  144. LOGGROUP(CloseDataModel)
  145. LOGGROUP(LegacyLock)
  146.  
  147. FASTFLAGVARIABLE(UserBetterInertialScrolling, false)
  148.  
  149. DYNAMIC_FASTINT(SavePlacePerMinute)
  150.  
  151. DYNAMIC_FASTINTVARIABLE(OnCloseTimeoutInSeconds, 30)
  152. DYNAMIC_FASTFLAGVARIABLE(AllowHideHudShortcut, false)
  153. DYNAMIC_FASTFLAGVARIABLE(AllowHideHudShortcutDefault, true)
  154. DYNAMIC_FASTFLAGVARIABLE(ProcessAcceleratorsBeforeGUINavigation, false)
  155. DYNAMIC_FASTFLAGVARIABLE(DontProcessMouseEventsForGuiTarget, false)
  156. DYNAMIC_FASTFLAGVARIABLE(CloseStatesBeforeChildRemoval, false)
  157. DYNAMIC_FASTFLAG(MaterialPropertiesEnabled);
  158. DYNAMIC_FASTFLAGVARIABLE(DataModelProcessHttpRequestResponseOnLockUseSubmitTask, true)
  159.  
  160. FASTFLAGVARIABLE(RelativisticCameraFixEnable, true)
  161. FASTFLAG(UserAllCamerasInLua)
  162.  
  163. LOGVARIABLE(CyclicExecutiveTiming, 0)
  164.  
  165. LOGVARIABLE(CyclicExecutiveThrottling, 0)
  166.  
  167. DYNAMIC_FASTFLAG(UseR15Character)
  168. DYNAMIC_LOGVARIABLE(R15Character, 0)
  169.  
  170. namespace RBX {
  171.  
  172. static void dummyLoader(RBX::DataModel*) {}
  173. int DataModel::LegacyLock::mainThreadId = ~0;
  174.  
  175. // DataModel::throttleAt30Fps should only affect the frequency at which Physics and Rendering runs. Nothing else.
  176. bool DataModel::throttleAt30Fps = true; // for debugging/benchmarking - default is true;
  177. unsigned int DataModel::sendStats = 0; // flipped if client is "hacking"
  178. std::string DataModel::hash; // contains the hash of the exe
  179. boost::function<void(RBX::DataModel*)> DataModel::loaderFunc = boost::function<void(RBX::DataModel*)>(dummyLoader);
  180.  
  181. REFLECTION_BEGIN();
  182. static Reflection::BoundFuncDesc<DataModel, void()> func_loadPlugins(&DataModel::loadPlugins, "LoadPlugins", Security::Roblox);
  183.  
  184. Reflection::EventDesc<DataModel, void(shared_ptr<Instance>, const Reflection::PropertyDescriptor*)> event_ItemChanged(&DataModel::itemChangedSignal, "ItemChanged", "object", "descriptor");
  185.  
  186. #if defined(RBX_STUDIO_BUILD) || defined (RBX_RCC_SECURITY) || defined (RBX_TEST_BUILD)
  187. static Reflection::BoundFuncDesc<DataModel, shared_ptr<const Instances>(ContentId)> getContentFunctionOld(&DataModel::fetchAsset, "get", "url", Security::LocalUser);
  188. static Reflection::BoundFuncDesc<DataModel, shared_ptr<const Instances>(ContentId)> getContentFunction(&DataModel::fetchAsset, "GetObjects", "url", Security::Plugin);
  189. #endif
  190.  
  191. static const Reflection::BoundYieldFuncDesc<DataModel, bool(Instance::SaveFilter)> savePlaceAsyncFunction(&DataModel::savePlaceAsync, "SavePlace", "saveFilter", DataModel::SAVE_ALL, Security::None);
  192.  
  193. static Reflection::BoundFuncDesc<DataModel, void(int)> loadWorldFunction(&DataModel::loadWorld, "LoadWorld", "assetID", Security::LocalUser);
  194. static Reflection::BoundFuncDesc<DataModel, void(int)> loadGameFunction(&DataModel::loadGame, "LoadGame", "assetID", Security::LocalUser);
  195.  
  196. static Reflection::BoundFuncDesc<DataModel, void(ContentId)> loadFunction(&DataModel::loadContent, "Load", "url", Security::LocalUser);
  197. static Reflection::BoundFuncDesc<DataModel, void(ContentId)> saveFunction(&DataModel::save, "Save", "url", Security::Roblox);
  198.  
  199. static Reflection::BoundFuncDesc<DataModel, void(bool)> setRemoteBuildMode(&DataModel::setRemoteBuildMode, "SetRemoteBuildMode", "buildModeEnabled", Security::LocalUser);
  200. static Reflection::BoundFuncDesc<DataModel, bool()> getRemoteBuildMode(&DataModel::getRemoteBuildMode, "GetRemoteBuildMode", Security::None);
  201. static Reflection::BoundFuncDesc<DataModel, void(std::string)> setServerSaveUrlFunction(&DataModel::setServerSaveUrl, "SetServerSaveUrl", "url", Security::LocalUser);
  202. static Reflection::BoundFuncDesc<DataModel, void()> serverSaveFunction(&DataModel::serverSave, "ServerSave", Security::LocalUser);
  203.  
  204. // TODO: Security: Make this Trusted only:
  205. // TODO: Remove the default value for synchronous, or make it true? It is non-intuitive to be false by default
  206. static const Security::Permissions kHttpPermission = Security::RobloxScript;
  207. static Reflection::BoundYieldFuncDesc<DataModel, std::string(std::string)> httpGetAsyncFunction(&DataModel::httpGetAsync, "HttpGetAsync", "url", kHttpPermission);
  208. static Reflection::BoundYieldFuncDesc<DataModel, std::string(std::string, std::string, std::string)> httpPostAsyncFunction(&DataModel::httpPostAsync, "HttpPostAsync", "url", "data", "contentType", "*/*", kHttpPermission);
  209. static Reflection::BoundFuncDesc<DataModel, std::string(std::string, bool)> httpGetFunction(&DataModel::httpGet, "HttpGet", "url", "synchronous", false, kHttpPermission);
  210. static Reflection::BoundFuncDesc<DataModel, std::string(std::string, std::string, bool, std::string)> httpPostFunction(&DataModel::httpPost, "HttpPost", "url", "data", "synchronous", false, "contentType", "*/*", kHttpPermission);
  211.  
  212. static Reflection::BoundFuncDesc<DataModel, shared_ptr<const Reflection::ValueArray>()> getJobsInfo(&DataModel::getJobsInfo, "GetJobsInfo", Security::Plugin);
  213. static Reflection::BoundFuncDesc<DataModel, void(std::string, std::string, std::string, std::string, std::string)> func_reportMeasurement(&DataModel::reportMeasurement, "ReportMeasurement", "id", "key1", "value1", "key2", "value2", Security::RobloxScript);
  214. static Reflection::BoundFuncDesc<DataModel, void(std::string, std::string, std::string, int)> googleAnalyticsFunction(&DataModel::luaReportGoogleAnalytics, "ReportInGoogleAnalytics", "category", "action", "custom", "label", "none", "value", 0, Security::RobloxScript);
  215.  
  216. #if defined(RBX_STUDIO_BUILD) || defined (RBX_RCC_SECURITY) || defined (RBX_TEST_BUILD)
  217. static Reflection::BoundFuncDesc<DataModel, void(bool)> sanitizeFunction(&DataModel::clearContents, "ClearContent", "resettingSimulation", Security::LocalUser);
  218. #endif
  219. static Reflection::BoundFuncDesc<DataModel, void()> closeFunction(&DataModel::close, "Shutdown", Security::LocalUser);
  220. static Reflection::BoundFuncDesc<DataModel, void()> toggleFunction(&DataModel::toggleToolsOff, "ToggleTools", Security::LocalUser);
  221.  
  222. static Reflection::PropDescriptor<DataModel, bool> prop_isPersonalServer("IsPersonalServer", category_State, &DataModel::getIsPersonalServer, &DataModel::setIsPersonalServer, Reflection::PropertyDescriptor::SCRIPTING, Security::RobloxScript);
  223. static Reflection::PropDescriptor<DataModel, bool> prop_canSaveLocal("LocalSaveEnabled", category_State, &DataModel::canSaveLocal, NULL, Reflection::PropertyDescriptor::UI, Security::RobloxScript);
  224.  
  225. static Reflection::BoundYieldFuncDesc<DataModel, bool()> saveToRobloxFunction(&DataModel::saveToRoblox, "SaveToRoblox", Security::RobloxScript);
  226. static Reflection::BoundCallbackDesc<bool()> requestShutdownCallback("RequestShutdown", &DataModel::requestShutdownCallback, Security::RobloxScript);
  227. static Reflection::BoundFuncDesc<DataModel, void(bool)> finishShutdownFunction(&DataModel::completeShutdown, "FinishShutdown", "localSave", Security::RobloxScript);
  228.  
  229. static Reflection::BoundFuncDesc<DataModel, void(std::string)> func_SetUiMessage(&DataModel::setUiMessage, "SetMessage", "message", Security::LocalUser);
  230. static Reflection::BoundFuncDesc<DataModel, void()> func_ClearUIMessage(&DataModel::clearUiMessage, "ClearMessage", Security::LocalUser);
  231. static Reflection::BoundFuncDesc<DataModel, void()> func_SetUIMessageBrickCount(&DataModel::setUiMessageBrickCount, "SetMessageBrickCount", Security::LocalUser);
  232. static Reflection::BoundFuncDesc<DataModel, std::string()> func_GetUIMessage(&DataModel::getUpdatedMessageBoxText, "GetMessage", Security::None);
  233.  
  234. static Reflection::BoundFuncDesc<DataModel, shared_ptr<const Reflection::ValueArray>()> getJobsExtendedStats(&DataModel::getJobsExtendedStats, "GetJobsExtendedStats", Security::Plugin);
  235. static Reflection::BoundFuncDesc<DataModel, double(std::string, double)> getJobTimePeakFraction(&DataModel::getJobTimePeakFraction, "GetJobTimePeakFraction", "jobname", "greaterThan", Security::Plugin);
  236. static Reflection::BoundFuncDesc<DataModel, double(std::string, double)> getJobIntervalPeakFraction(&DataModel::getJobIntervalPeakFraction, "GetJobIntervalPeakFraction", "jobname", "greaterThan", Security::Plugin);
  237. // TODO: Make this part of settings, not per-document. After all, it is a global setting
  238. static Reflection::BoundFuncDesc<DataModel, void(double)> setJobsExtendedStatsWindow(&DataModel::setJobsExtendedStatsWindow, "SetJobsExtendedStatsWindow", "seconds", Security::LocalUser);
  239.  
  240. static Reflection::BoundFuncDesc<DataModel, void(int)> setPlaceVersion(&DataModel::setPlaceVersion, "SetPlaceVersion", "placeId", Security::Plugin);
  241. static Reflection::BoundFuncDesc<DataModel, void(int, bool)> setPlaceId(&DataModel::setPlaceID, "SetPlaceId", "placeId", "robloxPlace", false, Security::Plugin);
  242. static Reflection::BoundFuncDesc<DataModel, void(int, bool)> setPlaceIdDeprecated(&DataModel::setPlaceID, "SetPlaceID", "placeID", "robloxPlace", false, Security::Plugin);
  243. static Reflection::BoundFuncDesc<DataModel, void(std::string)> setGameInstanceId(&DataModel::setGameInstanceID, "SetGameInstanceId", "instanceID", Security::Plugin);
  244. static Reflection::BoundFuncDesc<DataModel, void(int)> setUniverseId(&DataModel::setUniverseId, "SetUniverseId", "universeId", Security::Plugin);
  245. static Reflection::BoundFuncDesc<DataModel, void(int, DataModel::CreatorType)> setCreatorId(&DataModel::setCreatorID, "SetCreatorId", "creatorId", "creatorType", Security::Plugin);
  246. static Reflection::BoundFuncDesc<DataModel, void(int, DataModel::CreatorType)> setCreatorIdDeprecated(&DataModel::setCreatorID, "SetCreatorID", "creatorID", "creatorType", Security::Plugin);
  247. static Reflection::BoundFuncDesc<DataModel, void(DataModel::Genre)> func_setGenre(&DataModel::setGenre, "SetGenre", "genre", Security::Plugin);
  248. static Reflection::BoundFuncDesc<DataModel, void(DataModel::GearGenreSetting, int)> func_setGear(&DataModel::setGear, "SetGearSettings", "genreRestriction", "allowedGenres", Security::Plugin);
  249.  
  250. static Reflection::RefPropDescriptor<DataModel, Workspace> prop_Workspace("Workspace", category_Data, &DataModel::getWorkspace, NULL, Reflection::PropertyDescriptor::UI);
  251. static Reflection::RefPropDescriptor<DataModel, Workspace> prop_workspace("workspace", category_Data, &DataModel::getWorkspace, NULL, Reflection::PropertyDescriptor::Attributes::deprecated(prop_Workspace));
  252. Reflection::RefPropDescriptor<DataModel, Instance> DataModel::prop_lighting("lighting", category_Data, &DataModel::getLightingDeprecated, NULL, Reflection::PropertyDescriptor::Attributes::deprecated());
  253.  
  254. static Reflection::PropDescriptor<DataModel, int> prop_placeId("PlaceId", category_State, &DataModel::getPlaceID, NULL, Reflection::PropertyDescriptor::UI);
  255. static Reflection::PropDescriptor<DataModel, int> prop_placeVersion("PlaceVersion", category_State, &DataModel::getPlaceVersion, NULL, Reflection::PropertyDescriptor::UI);
  256. static Reflection::PropDescriptor<DataModel, int> prop_creatorId("CreatorId", category_State, &DataModel::getCreatorID, NULL, Reflection::PropertyDescriptor::UI);
  257. static Reflection::PropDescriptor<DataModel, bool> prop_forceR15("ForceR15", category_State, &DataModel::getForceR15, &DataModel::setForceR15, Reflection::PropertyDescriptor::REPLICATE_ONLY, Security::Roblox);
  258. static Reflection::EnumPropDescriptor<DataModel, DataModel::CreatorType> prop_creatorType("CreatorType", category_State, &DataModel::getCreatorType, NULL, Reflection::PropertyDescriptor::UI);
  259. static Reflection::EnumPropDescriptor<DataModel, DataModel::Genre> prop_genre("Genre", category_State, &DataModel::getGenre, NULL, Reflection::PropertyDescriptor::UI);
  260. static Reflection::EnumPropDescriptor<DataModel, DataModel::GearGenreSetting> prop_gearGenreSetting("GearGenreSetting", category_State, &DataModel::getGearGenreSetting, NULL, Reflection::PropertyDescriptor::UI);
  261. static Reflection::BoundFuncDesc<DataModel, bool(DataModel::GearType)> func_isGearTypeAllowed(&DataModel::isGearTypeAllowed, "IsGearTypeAllowed", "gearType", Security::None);
  262. static Reflection::EventDesc<DataModel, void()> event_allowedGearTypeChanged(&DataModel::allowedGearTypeChanged, "AllowedGearTypeChanged");
  263.  
  264. static Reflection::PropDescriptor<DataModel, std::string> prop_getJobId("JobId", "JobInfo", &DataModel::getJobId, NULL, Reflection::PropertyDescriptor::UI);
  265.  
  266. static Reflection::BoundFuncDesc<DataModel, void(std::string)> setScreenshotSEOInfoFunction(&DataModel::setScreenshotSEOInfo, "SetScreenshotInfo", "info", Security::LocalUser);
  267. static Reflection::BoundFuncDesc<DataModel, void(std::string)> setVideoSEOInfoFunction(&DataModel::setVideoSEOInfo, "SetVideoInfo", "info", Security::LocalUser);
  268.  
  269. static Reflection::EventDesc<DataModel, void(bool)> event_graphicsQualityChangeRequest(&DataModel::graphicsQualityShortcutSignal, "GraphicsQualityChangeRequest","betterQuality");
  270.  
  271. static Reflection::EventDesc<DataModel, void()> event_GameLoaded(&DataModel::gameLoadedSignal, "Loaded");
  272. static Reflection::BoundFuncDesc<DataModel, bool()> gameLoadedFunction(&DataModel::getIsGameLoaded, "IsLoaded", Security::None);
  273.  
  274. static Reflection::BoundFuncDesc<DataModel, void(std::string, std::string)> addStatFunction(&DataModel::addCustomStat, "AddStat", "displayName", "stat", Security::LocalUser);
  275. static Reflection::BoundFuncDesc<DataModel, void(std::string)> removeStatFunction(&DataModel::removeCustomStat, "RemoveStat", "stat", Security::LocalUser);
  276. static Reflection::BoundFuncDesc<DataModel, void()> writeStatSettingsFunction(&DataModel::writeStatsSettings, "SaveStats", Security::LocalUser);
  277.  
  278. static Reflection::BoundAsyncCallbackDesc<
  279. DataModel, shared_ptr<const Reflection::Tuple>(void)> callback_onClose("OnClose", &DataModel::onCloseCallback, &DataModel::onCloseCallbackChanged);
  280.  
  281. static Reflection::PropDescriptor<DataModel, std::string> desc_VIPServerId("VIPServerId", category_Data, &DataModel::getVIPServerId, NULL);
  282. static Reflection::BoundFuncDesc<DataModel, void(std::string)> func_setVIPServerId(&DataModel::setVIPServerId, "SetVIPServerId", "newId", Security::LocalUser);
  283. static Reflection::PropDescriptor<DataModel, int> desc_VIPServerOwnerId("VIPServerOwnerId", category_Data, &DataModel::getVIPServerOwnerId, NULL);
  284. static Reflection::BoundFuncDesc<DataModel, void(int)> func_setVIPServerOwnerId(&DataModel::setVIPServerOwnerId, "SetVIPServerOwnerId", "newId", Security::LocalUser);
  285. REFLECTION_END();
  286.  
  287. bool DataModel::BlockingDataModelShutdown = true;
  288. unsigned int DataModel::perfStats = 0; // flipped if client is "hacking"
  289.  
  290. const char *const sDataModel = "DataModel";
  291.  
  292. namespace Reflection
  293. {
  294. template<>
  295. EnumDesc<DataModel::CreatorType>::EnumDesc()
  296. :EnumDescriptor("CreatorType")
  297. {
  298. addPair(DataModel::CREATOR_USER, "User");
  299. addPair(DataModel::CREATOR_GROUP, "Group");
  300. }
  301.  
  302. template<>
  303. DataModel::CreatorType& Variant::convert<DataModel::CreatorType>(void)
  304. {
  305. return genericConvert<DataModel::CreatorType>();
  306. }
  307.  
  308.  
  309. template<>
  310. EnumDesc<DataModel::Genre>::EnumDesc()
  311. :EnumDescriptor("Genre")
  312. {
  313. addPair(DataModel::GENRE_ALL , "All");
  314. addPair(DataModel::GENRE_TOWN_AND_CITY , "TownAndCity");
  315. addPair(DataModel::GENRE_FANTASY , "Fantasy");
  316. addPair(DataModel::GENRE_SCI_FI , "SciFi");
  317. addPair(DataModel::GENRE_NINJA , "Ninja");
  318. addPair(DataModel::GENRE_SCARY , "Scary");
  319. addPair(DataModel::GENRE_PIRATE , "Pirate");
  320. addPair(DataModel::GENRE_ADVENTURE , "Adventure");
  321. addPair(DataModel::GENRE_SPORTS , "Sports");
  322. addPair(DataModel::GENRE_FUNNY , "Funny");
  323. addPair(DataModel::GENRE_WILD_WEST , "WildWest");
  324. addPair(DataModel::GENRE_WAR , "War");
  325. addPair(DataModel::GENRE_SKATE_PARK , "SkatePark");
  326. addPair(DataModel::GENRE_TUTORIAL , "Tutorial");
  327. }
  328.  
  329. template<>
  330. DataModel::Genre& Variant::convert<DataModel::Genre>(void)
  331. {
  332. return genericConvert<DataModel::Genre>();
  333. }
  334.  
  335.  
  336. template<>
  337. EnumDesc<DataModel::GearGenreSetting>::EnumDesc()
  338. :EnumDescriptor("GearGenreSetting")
  339. {
  340. addPair(DataModel::GEAR_GENRE_ALL, "AllGenres");
  341. addPair(DataModel::GEAR_GENRE_MATCH, "MatchingGenreOnly");
  342. }
  343.  
  344. template<>
  345. DataModel::GearGenreSetting& Variant::convert<DataModel::GearGenreSetting>(void)
  346. {
  347. return genericConvert<DataModel::GearGenreSetting>();
  348. }
  349.  
  350.  
  351. template<>
  352. EnumDesc<DataModel::GearType>::EnumDesc()
  353. :EnumDescriptor("GearType")
  354. {
  355. addPair(DataModel::GEAR_TYPE_MELEE_WEAPONS , "MeleeWeapons");
  356. addPair(DataModel::GEAR_TYPE_RANGED_WEAPONS , "RangedWeapons");
  357. addPair(DataModel::GEAR_TYPE_EXPLOSIVES , "Explosives");
  358. addPair(DataModel::GEAR_TYPE_POWER_UPS , "PowerUps");
  359. addPair(DataModel::GEAR_TYPE_NAVIGATION_ENHANCERS , "NavigationEnhancers");
  360. addPair(DataModel::GEAR_TYPE_MUSICAL_INSTRUMENTS , "MusicalInstruments");
  361. addPair(DataModel::GEAR_TYPE_SOCIAL_ITEMS , "SocialItems");
  362. addPair(DataModel::GEAR_TYPE_BUILDING_TOOLS , "BuildingTools");
  363. addPair(DataModel::GEAR_TYPE_PERSONAL_TRANSPORT , "Transport");
  364. }
  365.  
  366. template<>
  367. DataModel::GearType& Variant::convert<DataModel::GearType>(void)
  368. {
  369. return genericConvert<DataModel::GearType>();
  370. }
  371.  
  372. template<>
  373. EnumDesc<Instance::SaveFilter>::EnumDesc()
  374. :EnumDescriptor("SaveFilter")
  375. {
  376. addPair(Instance::SAVE_ALL , "SaveAll");
  377. addPair(Instance::SAVE_WORLD , "SaveWorld");
  378. addPair(Instance::SAVE_GAME , "SaveGame");
  379. }
  380.  
  381. template<>
  382. Instance::SaveFilter& Variant::convert<Instance::SaveFilter>(void)
  383. {
  384. return genericConvert<Instance::SaveFilter>();
  385. }
  386. }
  387.  
  388. template<>
  389. bool RBX::StringConverter<DataModel::CreatorType>::convertToValue(const std::string& text, DataModel::CreatorType& value)
  390. {
  391. return Reflection::EnumDesc<DataModel::CreatorType>::singleton().convertToValue(text.c_str(),value);
  392. }
  393.  
  394. template<>
  395. bool RBX::StringConverter<DataModel::Genre>::convertToValue(const std::string& text, DataModel::Genre& value)
  396. {
  397. return Reflection::EnumDesc<DataModel::Genre>::singleton().convertToValue(text.c_str(),value);
  398. }
  399.  
  400. template<>
  401. bool RBX::StringConverter<DataModel::GearGenreSetting>::convertToValue(const std::string& text, DataModel::GearGenreSetting& value)
  402. {
  403. return Reflection::EnumDesc<DataModel::GearGenreSetting>::singleton().convertToValue(text.c_str(),value);
  404. }
  405.  
  406. template<>
  407. bool RBX::StringConverter<DataModel::GearType>::convertToValue(const std::string& text, DataModel::GearType& value)
  408. {
  409. return Reflection::EnumDesc<DataModel::GearType>::singleton().convertToValue(text.c_str(),value);
  410. }
  411.  
  412. template<>
  413. bool RBX::StringConverter<DataModel::SaveFilter>::convertToValue(const std::string& text, DataModel::SaveFilter& value)
  414. {
  415. return Reflection::EnumDesc<DataModel::SaveFilter>::singleton().convertToValue(text.c_str(),value);
  416. }
  417.  
  418.  
  419. class DataModel::GenericJob : public DataModelJob
  420. {
  421. public:
  422. weak_ptr<DataModel> const dataModel;
  423. rbx::timestamped_safe_queue<Task> tasks;
  424.  
  425. GenericJob(shared_ptr<DataModel> dataModel, const char* name, TaskType taskType)
  426. :DataModelJob(name, taskType, false, dataModel, Time::Interval(0))
  427. ,dataModel(dataModel)
  428. {
  429. }
  430. private:
  431. virtual Time::Interval sleepTime(const Stats& stats)
  432. {
  433. return tasks.size() > 0 ? Time::Interval(0) : Time::Interval::max();
  434. }
  435.  
  436. void step(Task& task)
  437. {
  438. shared_ptr<DataModel> dm(dataModel.lock());
  439. if (!dm)
  440. return;
  441.  
  442. task(dm.get());
  443. }
  444.  
  445. //
  446. // *** THIS IS BEING USED AS A NEAR *IMMEDIATE* CALLBACK BY THE LEGACY LOCK. ***
  447. // *** HOWEVER THE ERROR CALCUALTION IS GARBAGE FOR THAT, INITIALLY RETURNING ZERO. ***
  448. //
  449. virtual Job::Error error(const Stats& stats)
  450. {
  451. Error result;
  452.  
  453. const int size = tasks.size();
  454. if (size)
  455. {
  456. // Estimate the total error to be headTime * n / 2
  457. result.error = tasks.head_waittime_sec(stats.timeNow) * (1 + size) / 2.0;
  458.  
  459. // These tasks are "urgent" because they are often used
  460. // with LegacyLock to marshal tasks with the UI or Web Service requests
  461. result.urgent = size > 0;
  462. }
  463. return result;
  464. }
  465.  
  466. virtual TaskScheduler::StepResult stepDataModelJob(const Stats& stats)
  467. {
  468. FASTLOG3(FLog::TaskSchedulerRun, "GenericJob::Step dataModel(%d) type(%d) taskCount(%d)", dataModel.lock().get(), taskType, tasks.size());
  469.  
  470. switch (taskType)
  471. {
  472. case Write:
  473. case DataIn:
  474. case PhysicsIn:
  475. {
  476. shared_ptr<DataModel> dm(dataModel.lock());
  477. if (!dm)
  478. return TaskScheduler::Done;
  479. // TODO: clean up, non-scoped write request?
  480. DataModel::scoped_write_request request(dm.get());
  481.  
  482. // need to call processTasks here because of the scoped write request
  483. processTasks();
  484. }
  485. break;
  486. default:
  487. {
  488. processTasks();
  489. }
  490. break;
  491. }
  492. return TaskScheduler::Stepped;
  493. }
  494.  
  495. void processTasks()
  496. {
  497. FASTLOG(FLog::DataModelJobs, "Process tasks start");
  498. Task task;
  499. int size = tasks.size();
  500. int count = 0;
  501. while ((count < size) && (tasks.pop_if_present(task))) // need to keep count because task can add more tasks
  502. {
  503. step(task);
  504. count++;
  505.  
  506. if (TaskScheduler::singleton().isCyclicExecutive())
  507. {
  508. continue;
  509. }
  510.  
  511. RBX::Time timeNow = RBX::Time::now<RBX::Time::Fast>();
  512. if (TaskScheduler::priorityMethod != TaskScheduler::FIFO)
  513. break;
  514. else if (tasks.head_waittime_sec(timeNow) < 0.2)
  515. {
  516. // FIFO mode: if we are able to process incoming tasks in a timely manner, process remaining tasks next frame
  517. break;
  518. }
  519. }
  520. FASTLOG(FLog::DataModelJobs, "Process tasks finish");
  521. }
  522. };
  523.  
  524. static std::string tempTag()
  525. {
  526. static rbx::atomic<int> count = 0;
  527. return RBX::format("DataModel-%d", (int)++count);
  528. }
  529.  
  530. bool DataModel::canSave(const RBX::Instance* instance)
  531. {
  532. //If the UploadURL is empty, they don't have permissions to save. Shut. It. Down.
  533. if(Visit* visit = ServiceProvider::find<Visit>(instance))
  534. return !visit->getUploadUrl().empty();
  535.  
  536. return true;
  537. }
  538.  
  539. bool DataModel::serverSavePlace(const SaveFilter saveFilter, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
  540. {
  541. // construct the url
  542. std::string parameters = "?assetId=" + boost::lexical_cast<std::string>(placeID);
  543. parameters += "&isAppCreation=true";
  544. std::string baseUrl = ServiceProvider::create<ContentProvider>(this)->getBaseUrl();
  545.  
  546. // save the place
  547. return uploadPlace(baseUrl + "ide/publish/UploadExistingAsset" + parameters, saveFilter, boost::bind(resumeFunction, true), errorFunction);
  548. }
  549.  
  550. namespace {
  551. static void sendSavePlaceStats()
  552. {
  553. RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_GAME, "SavePlace");
  554. }
  555. } // namespace
  556.  
  557. void DataModel::savePlaceAsync(const SaveFilter saveFilter, boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
  558. {
  559. if (placeID <= 0)
  560. {
  561. errorFunction("Game:SavePlace placeID is not valid!");
  562. return;
  563. }
  564.  
  565. if(!savePlaceThrottle.checkLimit())
  566. {
  567. errorFunction("Game:SavePlace requests limit reached");
  568. return;
  569. }
  570.  
  571. if(Network::Players::frontendProcessing(this))
  572. {
  573. errorFunction("Game:SavePlace can only be called from a server script, aborting save function");
  574. return;
  575. }
  576. else if(Network::Players::backendProcessing(this))
  577. {
  578. {
  579. static boost::once_flag flag = BOOST_ONCE_INIT;
  580. boost::call_once(&sendSavePlaceStats, flag);
  581. }
  582. serverSavePlace(saveFilter, resumeFunction, errorFunction);
  583.  
  584. }
  585. else
  586. {
  587. errorFunction("Game:SavePlace could not determine if client or server");
  588. return;
  589. }
  590. }
  591.  
  592. void DataModel::completeShutdown(bool saveLocal)
  593. {
  594. if(shutdownRequestedCount > 0){
  595. if(saveLocal && canSave(this)){
  596. if(Verb* verb = workspace->getWhitelistVerb("Shutdown", "Client", "AndSave")){
  597. Verb::doItWithChecks(verb, NULL);
  598. }
  599. }
  600. else{
  601. if(Verb* verb = workspace->getWhitelistVerb("Shutdown", "Client", "")){
  602. Verb::doItWithChecks(verb, NULL);
  603. }
  604. }
  605. }
  606. }
  607.  
  608. DataModel::RequestShutdownResult DataModel::requestShutdown(bool useLuaShutdownForSave)
  609. {
  610. if(!canSave(this))
  611. {
  612. Verb* verb = getWorkspace()->getWhitelistVerb("Toggle", "Play", "Mode");
  613. if ( verb && verb->isChecked() )
  614. return CLOSE_LOCAL_SAVE;
  615.  
  616. //We are in play mode... so don't save
  617. return CLOSE_NO_SAVE_NEEDED;
  618. }
  619.  
  620. Visit* visit = RBX::ServiceProvider::find<Visit>(this);
  621. if (!visit || visit->getUploadUrl().empty())
  622. {
  623. return CLOSE_LOCAL_SAVE;
  624. }
  625.  
  626. if(useLuaShutdownForSave && requestShutdownCallback){
  627. try
  628. {
  629. shutdownRequestedCount++;
  630. return requestShutdownCallback() ? CLOSE_REQUEST_HANDLED : CLOSE_NOT_HANDLED;
  631. }
  632. catch(RBX::base_exception&)
  633. {
  634. return CLOSE_NOT_HANDLED;
  635. }
  636. }
  637.  
  638. return CLOSE_NOT_HANDLED;
  639. }
  640.  
  641. TaskScheduler::Arbiter* DataModel::getSyncronizationArbiter()
  642. {
  643. return this;
  644. }
  645.  
  646. void DataModel::doDataModelSetup(shared_ptr<DataModel> dataModel, bool startHeartbeat, bool shouldShowLoadingScreen)
  647. {
  648. shared_ptr<DataModel::GenericJob> j;
  649. j = shared_ptr<DataModel::GenericJob>(new DataModel::GenericJob(dataModel, "Write Marshalled", DataModelJob::Write));
  650. j->cyclicExecutive = false;
  651. dataModel->genericJobs[DataModelJob::Write] = j;
  652. TaskScheduler::singleton().add(j);
  653. j = shared_ptr<DataModel::GenericJob>(new DataModel::GenericJob(dataModel, "Read Marshalled", DataModelJob::Read));
  654. j->cyclicExecutive = false;
  655. dataModel->genericJobs[DataModelJob::Read] = j;
  656. TaskScheduler::singleton().add(j);
  657. j = shared_ptr<DataModel::GenericJob>(new DataModel::GenericJob(dataModel, "None Marshalled", DataModelJob::None));
  658. j->cyclicExecutive = false;
  659. dataModel->genericJobs[DataModelJob::None] = j;
  660. TaskScheduler::singleton().add(j);
  661.  
  662. // TODO: Do we need this lock here? If initializing has no side effects
  663. // like starting a task, then it shouldn't be necessary.
  664. LegacyLock lock(dataModel, DataModelJob::Write);
  665.  
  666. RBX::DataModel::LegacyLock::mainThreadId = GetCurrentThreadId();
  667.  
  668. dataModel->initializeContents(startHeartbeat);
  669. dataModel->isInitialized = true;
  670. dataModel->suppressNavKeys = false;
  671.  
  672. // get loading script going ASAP so it shows on first render
  673. if (shouldShowLoadingScreen && !(TeleportService::didTeleport() && TeleportService::getCustomTeleportLoadingGui()))
  674. {
  675. try
  676. {
  677. if (boost::optional<ProtectedString> source = CoreScript::fetchSource("LoadingScript"))
  678. {
  679. if (ScriptContext* sc = RBX::ServiceProvider::create<ScriptContext>(dataModel.get()))
  680. {
  681. sc->executeInNewThread(RBX::Security::RobloxGameScript_, *source, "LoadingScript");
  682. }
  683. }
  684. }
  685. catch(std::exception& e)
  686. {
  687. StandardOut::singleton()->printf(MESSAGE_ERROR, "LoadingScript Error: %s", e.what());
  688. }
  689. }
  690.  
  691. FASTLOG1(FLog::CloseDataModel, "DataModel created: %p", dataModel.get());
  692. }
  693.  
  694. shared_ptr<DataModel> DataModel::createDataModel(bool startHeartbeat, RBX::Verb* lockVerb, bool shouldShowLoadingScreen)
  695. {
  696. FASTLOG1(FLog::CloseDataModel, "Create DataModel - heartbeat(%d)", startHeartbeat);
  697.  
  698. shared_ptr<DataModel> dataModel = Creatable<Instance>::create<RBX::DataModel>(lockVerb);
  699.  
  700. doDataModelSetup(dataModel, startHeartbeat, shouldShowLoadingScreen);
  701. return dataModel;
  702. }
  703.  
  704. bool DataModel::onlyJobsLeftForThisArbiterAreGenericJobs() {
  705. std::vector<boost::shared_ptr<const TaskScheduler::Job> > jobs;
  706. TaskScheduler::singleton().getJobsInfo(jobs);
  707. std::vector<boost::shared_ptr<const TaskScheduler::Job> >::iterator itr =
  708. jobs.begin();
  709.  
  710. while (itr != jobs.end()) {
  711. if ((*itr)->getArbiter().get() == this) {
  712. bool anyMatch = false;
  713. for (int i = 0; i < DataModelJob::TaskTypeMax && !anyMatch; ++i) {
  714. if (genericJobs[i] == *itr) {
  715. anyMatch = true;
  716. }
  717. }
  718. RBXASSERT(anyMatch);
  719. if (!anyMatch) {
  720. return false;
  721. }
  722. }
  723. itr++;
  724. }
  725. return true;
  726. }
  727.  
  728. void DataModel::doCloseDataModel(shared_ptr<DataModel> dataModel)
  729. {
  730. FASTLOG1(FLog::CloseDataModel, "doCloseDataModel - %p", dataModel.get());
  731.  
  732. RBX::Security::Impersonator impersonate(RBX::Security::LocalGUI_);
  733.  
  734. // Turn off ChangeHistoryService to avoid unecessary recording of state
  735. if (ChangeHistoryService* ch = ServiceProvider::find<ChangeHistoryService>(dataModel.get()))
  736. ch->setEnabled(false);
  737.  
  738. FASTLOG(FLog::CloseDataModel, "Raising close..");
  739. dataModel->raiseClose(); // TODO: Demote this to MFC-only project???
  740.  
  741. RBXASSERT(dataModel->isInitialized);
  742. dataModel->isInitialized = false;
  743.  
  744. FASTLOG(FLog::CloseDataModel, "Removing all players");
  745. if(Network::Players* players = ServiceProvider::find<Network::Players>(dataModel.get()))
  746. players->removeAllChildren();
  747.  
  748. FASTLOG(FLog::CloseDataModel, "Clearing contents...");
  749. // Ensure that all content is wiped out
  750. dataModel->clearContents(false);
  751.  
  752.  
  753. FASTLOG(FLog::CloseDataModel, "Visiting children with unlockParent...");
  754. dataModel->visitChildren(boost::bind(&Instance::unlockParent, _1));
  755.  
  756. FASTLOG(FLog::CloseDataModel, "Removing all Children...");
  757. dataModel->removeAllChildren();
  758.  
  759. FASTLOG(FLog::CloseDataModel, "Resetting workspace...");
  760. dataModel->workspace.reset();
  761.  
  762. FASTLOG(FLog::CloseDataModel, "Resetting runService...");
  763. dataModel->runService.reset();
  764.  
  765. FASTLOG(FLog::CloseDataModel, "Resetting starterPackService...");
  766. dataModel->starterPackService.reset();
  767.  
  768. FASTLOG(FLog::CloseDataModel, "Resetting starterGuiService...");
  769. dataModel->starterGuiService.reset();
  770.  
  771. FASTLOG(FLog::CloseDataModel, "Resetting starterPlayerService...");
  772. dataModel->starterPlayerService.reset();
  773.  
  774. FASTLOG(FLog::CloseDataModel, "Resetting guiRoot...");
  775. dataModel->guiRoot.reset();
  776.  
  777. FASTLOG(FLog::CloseDataModel, "Clearing services...");
  778. dataModel->clearServices();
  779.  
  780. RBXASSERT(dataModel->onlyJobsLeftForThisArbiterAreGenericJobs());
  781.  
  782. FASTLOG(FLog::CloseDataModel, "Removing GenericJobs...");
  783. {
  784. bool blockingRemove = DataModel::BlockingDataModelShutdown && RBX::DebugSettings::singleton().blockingRemove;
  785. for (GenericJobs::iterator iter = dataModel->genericJobs.begin(); iter!=dataModel->genericJobs.end(); ++iter)
  786. {
  787. if(blockingRemove)
  788. TaskScheduler::singleton().removeBlocking(*iter);
  789. else
  790. TaskScheduler::singleton().remove(*iter);
  791. iter->reset();
  792. }
  793. }
  794.  
  795. FASTLOG(FLog::CloseDataModel, "Close DataModel Done!");
  796. }
  797.  
  798. static void closeCallbackContinuation(shared_ptr<CEvent> event)
  799. {
  800. FASTLOG(FLog::CloseDataModel, "Close Callback finished");
  801. FASTLOG1(FLog::CloseDataModel, "Close callback resets event - %p", event.get());
  802. event->Set();
  803. }
  804.  
  805. static void errorCallbackContinuation(std::string error, shared_ptr<CEvent> event)
  806. {
  807. FASTLOGS(FLog::CloseDataModel, "Close Callback error - %s", error);
  808. FASTLOG1(FLog::CloseDataModel, "Close callback resets event on error - %p", event.get());
  809. event->Set();
  810. }
  811.  
  812.  
  813. void DataModel::closeDataModel(shared_ptr<DataModel> dataModel)
  814. {
  815. FASTLOG1(FLog::CloseDataModel, "closeDataModel - %p", dataModel.get());
  816.  
  817. if (!dataModel)
  818. return;
  819.  
  820.  
  821. if(dataModel->onCloseCallback)
  822. {
  823. FASTLOG1(FLog::CloseDataModel, "Setting up onClose callback - %p", dataModel.get());
  824.  
  825. RBXASSERT(!dataModel->currentThreadHasWriteLock());
  826. shared_ptr<CEvent> waitEvent(new CEvent(false));
  827.  
  828. {
  829. LegacyLock lock(dataModel, DataModelJob::Write);
  830. if(Workspace::clientIsPresent(dataModel.get()))
  831. waitEvent->Set();
  832. else
  833. {
  834. FASTLOG(FLog::CloseDataModel, "Calling onClose callback");
  835. dataModel->onCloseCallback(boost::bind(closeCallbackContinuation, waitEvent), boost::bind(errorCallbackContinuation, _1, waitEvent));
  836. }
  837. }
  838.  
  839. bool result = waitEvent->Wait(Time::Interval((double)DFInt::OnCloseTimeoutInSeconds));
  840. FASTLOG1(FLog::CloseDataModel, "Wait returned %u", result);
  841. }
  842.  
  843. LegacyLock lock(dataModel, DataModelJob::Write);
  844. FASTLOG(FLog::CloseDataModel, "Finished waiting on lock");
  845. doCloseDataModel(dataModel);
  846. }
  847.  
  848. void DataModel::onCloseCallbackChanged(const CloseCallback& oldValue)
  849. {
  850. if(onCloseCallback && oldValue)
  851. {
  852. onCloseCallback = oldValue;
  853. throw RBX::runtime_error("OnClose is already set");
  854. }
  855. }
  856.  
  857. // TODO: Refactor: This class is HORRIBLE. GET RID OF THE BLOB
  858. // Also, it is dangerous to initialize all this stuff in the contructor
  859. // Because DataModel isn't owned by a shared_ptr yet. So, code that
  860. // calls shared_from(dataModel) will fail
  861. // My only idea at the moment to get around it is to have a DataModel::initializeContet
  862. // function, or perhaps wrap the whole thing into a static "CreateDataModel" function
  863. // that constructs DataModel, and creates essential services.
  864. // All these verbs should be taken out and put into a UI library (which isn't needed by
  865. // by the WebService, for example.
  866. DataModel::DataModel(RBX::Verb* lockVerb)
  867. :Super("Game"),
  868. VerbContainer(NULL),
  869. shutdownRequestedCount(0),
  870. lockVerb(lockVerb),
  871. write_requested(0),
  872. writeRequestingThread(0),
  873. read_requested(0),
  874. dirty(false),
  875. isInitialized(false),
  876. isContentLoaded(false),
  877. isShuttingDown(false),
  878. runningInStudio(false),
  879. isStudioRunMode(false),
  880. checkedExperimentalFeatures(false),
  881. drawId(0),
  882. placeID(0),
  883. gameInstanceID(""),
  884. placeVersion(0),
  885. creatorID(0),
  886. universeId(0),
  887. physicsStepID(0),
  888. totalWorldSteps(0),
  889. creatorType(DataModel::CREATOR_USER),
  890. remoteBuildMode(false),
  891. numPartInstances(0),
  892. numInstances(0),
  893. mouseOverGui(false),
  894. networkMetric(NULL),
  895. #pragma warning(push)
  896. #pragma warning(disable: 4355) // 'this' : used in base member initializer list
  897. workspace(Creatable<Instance>::create<Workspace>(this)),
  898. guiRoot(Creatable<Instance>::create<GuiRoot>()),
  899. forceArrowCursor(true),
  900. isGameLoaded(false),
  901. areCoreScriptsLoaded(false),
  902. game(NULL),
  903. networkStatsWindowsOn(false),
  904. #pragma warning(pop)
  905. dataModelInitTime(Time::nowFast()),
  906. savePlaceThrottle(&DFInt::SavePlacePerMinute),
  907. vipServerId(""),
  908. vipServerOwnerId(0),
  909. renderGuisActive(DFFlag::AllowHideHudShortcutDefault),
  910. canRequestUniverseInfo(false),
  911. universeDataRequested(false),
  912. forceR15(false)
  913. {
  914. if (TaskScheduler::singleton().isCyclicExecutive())
  915. {
  916. TaskScheduler::singleton().DataModel30fpsThrottle = DataModel::throttleAt30Fps;
  917. }
  918.  
  919.  
  920. count++;
  921. lockParent();
  922.  
  923. guiBuilder.Initialize(this);
  924. }
  925.  
  926. bool DataModel::getRemoteBuildMode()
  927. {
  928. return false;
  929. }
  930.  
  931. void DataModel::setRemoteBuildMode(bool remoteBuildMode)
  932. {
  933. }
  934.  
  935. void DataModel::setScreenshotSEOInfo(std::string value)
  936. {
  937. screenshotSEOInfo = value;
  938. }
  939.  
  940. void DataModel::setVideoSEOInfo(std::string value)
  941. {
  942. videoSEOInfo = value;
  943. }
  944.  
  945. std::string DataModel::getScreenshotSEOInfo()
  946. {
  947. return screenshotSEOInfo;
  948. }
  949.  
  950. // TODO: Refactor. this is gross. can we get away without it? Or put all other stuff here, too?
  951. void DataModel::initializeContents(bool startHeartbeat)
  952. {
  953. RBXASSERT(!isInitialized);
  954. workspace->setAndLockParent(this);
  955. guiRoot->setAndLockParent(this);
  956.  
  957. setIsPersonalServer(false);
  958.  
  959. // Add basic services that are always there
  960. ServiceProvider::create<NonReplicatedCSGDictionaryService>();
  961. ServiceProvider::create<CSGDictionaryService>();
  962. ServiceProvider::create<LogService>();
  963. ServiceProvider::create<ContentProvider>();
  964. ServiceProvider::create<ContentFilter>();
  965. ServiceProvider::create<KeyframeSequenceProvider>();
  966. ServiceProvider::create<GuiService>();
  967. ServiceProvider::create<ChatService>();
  968. ServiceProvider::create<MarketplaceService>();
  969. ServiceProvider::create<PointsService>();
  970. ServiceProvider::create<AdService>();
  971. ServiceProvider::create<NotificationService>();
  972. ServiceProvider::create<ReplicatedFirst>();
  973. ServiceProvider::create<HttpRbxApiService>();
  974.  
  975. starterPlayerService = shared_from(ServiceProvider::create<StarterPlayerService>());
  976.  
  977. starterPackService = shared_from(ServiceProvider::create<StarterPackService>());
  978.  
  979. starterGuiService = shared_from(ServiceProvider::create<StarterGuiService>());
  980.  
  981. coreGuiService = shared_from(ServiceProvider::create<CoreGuiService>());
  982. coreGuiService->createRobloxScreenGui();
  983.  
  984. if (TeleportService* ts = ServiceProvider::create<TeleportService>(this))
  985. {
  986. if (TeleportService::getCustomTeleportLoadingGui() && TeleportService::didTeleport())
  987. {
  988. if (RBX::ScreenGui* loadingGui = Instance::fastDynamicCast<RBX::ScreenGui>(TeleportService::getCustomTeleportLoadingGui().get()))
  989. {
  990. ts->setTempCustomTeleportLoadingGui(loadingGui->clone(EngineCreator));
  991. ts->getTempCustomTeleportLoadingGui()->setParent(coreGuiService->getRobloxScreenGui().get());
  992. ts->setCustomTeleportLoadingGui(loadingGui->clone(EngineCreator));
  993. }
  994. }
  995. }
  996.  
  997. runService = shared_from(ServiceProvider::create<RunService>());
  998.  
  999. ServiceProvider::create<Soundscape::SoundService>();
  1000.  
  1001. workspace->setDefaultMouseCommand();
  1002.  
  1003. workspace->setVerbParent(this);
  1004.  
  1005. isPersonalServer = false;
  1006.  
  1007. if (startHeartbeat)
  1008. runService->start();
  1009.  
  1010. // No need to save the connection. The run service will be deleted before we are
  1011. runService->runTransitionSignal.connect(boost::bind(&DataModel::onRunTransition, this, _1));
  1012.  
  1013. // TODO: Do we really need to pre-create these services? It adds #include hell to this file.
  1014. // Most services should be created on-demand, even in a Lua startup script, but not here!
  1015. ServiceProvider::create<JointsService>();
  1016. ServiceProvider::create<CollectionService>();
  1017. ServiceProvider::create<PhysicsService>();
  1018. ServiceProvider::create<BadgeService>();
  1019. ServiceProvider::create<GeometryService>();
  1020. ServiceProvider::create<FriendService>();
  1021. ServiceProvider::create<RenderHooksService>();
  1022. ServiceProvider::create<InsertService>();
  1023. ServiceProvider::create<SocialService>();
  1024. ServiceProvider::create<GamePassService>();
  1025. ServiceProvider::create<DebrisService>();
  1026. ServiceProvider::create<ScriptInformationProvider>();
  1027. ServiceProvider::create<CookiesService>();
  1028. ServiceProvider::create<TeleportService>();
  1029. ServiceProvider::create<PersonalServerService>();
  1030. ServiceProvider::create<Network::Players>(); // We always need this, because it saves state
  1031. userInputService = shared_from(ServiceProvider::create<UserInputService>());
  1032. contextActionService = shared_from(ServiceProvider::create<ContextActionService>()); // store a ref since we use this in processInputObject
  1033. ServiceProvider::create<ScriptService>();
  1034. ServiceProvider::create<AssetService>();
  1035.  
  1036. }
  1037.  
  1038. void DataModel::loadCoreScripts(const std::string &altStarterScript)
  1039. {
  1040. if (!areCoreScriptsLoaded)
  1041. {
  1042. areCoreScriptsLoaded = true;
  1043. std::string baseUrl = ServiceProvider::create<ContentProvider>(this)->getBaseUrl();
  1044.  
  1045. if (!baseUrl.empty() && baseUrl[baseUrl.size() - 1] != '/')
  1046. baseUrl += '/';
  1047.  
  1048. StudioConfigurer sc;
  1049. sc.starterScript = altStarterScript;
  1050. sc.configure(Security::COM, this, format("{ \"BaseUrl\": \"%s\" }", baseUrl.c_str()));
  1051. }
  1052. }
  1053.  
  1054.  
  1055. void DataModel::startCoreScripts(bool buildInGameGui, const std::string &altStarterScript)
  1056. {
  1057. // Build Gui - all verbs should be set
  1058. guiBuilder.buildGui(this->workspace.get(), buildInGameGui);
  1059. loadCoreScripts(altStarterScript);
  1060. }
  1061.  
  1062.  
  1063. rbx::atomic<int> DataModel::count;
  1064.  
  1065. DataModel::~DataModel()
  1066. {
  1067. count--;
  1068. if(placeID != 0)
  1069. RbxDbgInfo::RemovePlace(placeID);
  1070. }
  1071.  
  1072.  
  1073.  
  1074. struct DataModel::LegacyLock::Implementation : boost::noncopyable
  1075. {
  1076. // To marshal the task to this thread we use a pair of locks (acquire and release)
  1077. // Since creating mutexes are expensive, we push spent Events into a pool
  1078. // for re-use
  1079. struct Events
  1080. {
  1081. CEvent acquiredLock;
  1082. CEvent releasedLock;
  1083. Events()
  1084. :acquiredLock(false)
  1085. ,releasedLock(false)
  1086. {}
  1087. };
  1088. SAFE_STATIC(rbx::safe_queue< shared_ptr<Events> >, eventsPool)
  1089.  
  1090. // The job in this thread that has currently acquired a lock (if any)
  1091. // This thread specific pointer is used to re-entrancy checks
  1092. SAFE_STATIC(rbx::thread_specific_reference<GenericJob>, currentJob)
  1093.  
  1094. DataModel* const dataModel;
  1095.  
  1096. // The job being used for scheduling:
  1097. GenericJob* job;
  1098. // The previous job, if any, being used for scheduling:
  1099. GenericJob* previousJob;
  1100. // The mutexes used to marshal the task across threads:
  1101. shared_ptr<Events> events;
  1102.  
  1103. boost::scoped_ptr<DataModel::scoped_write_transfer> writeTransfer;
  1104.  
  1105. static void task(shared_ptr<Events> events)
  1106. {
  1107. RBXPROFILER_SCOPE("Jobs", "LegacyLock");
  1108.  
  1109. // Signal the thread that constructed the Implementation object to proceed
  1110. events->acquiredLock.Set();
  1111.  
  1112. // Wait for Implementation object to go out of scope (task complete)
  1113. events->releasedLock.Wait();
  1114.  
  1115. // Recycle the events pair
  1116. eventsPool().push(events);
  1117. }
  1118.  
  1119. Implementation(DataModel* dataModel, boost::shared_ptr<GenericJob> job)
  1120. :job(job.get())
  1121. ,dataModel(dataModel)
  1122. {
  1123. RBXASSERT(job);
  1124. previousJob = currentJob().get();
  1125. // Re-entrancy test. No need to wait if we're recursively locked on this DataModel
  1126. if (this->job != previousJob)
  1127. {
  1128. FASTLOG2(FLog::LegacyLock, "LegacyLock::Begin, DataModel: (%p), type(%d)", dataModel, job->taskType);
  1129. // Set the current job for this thread
  1130. currentJob().reset(this->job);
  1131.  
  1132. // Get/create a pair of events for marshalling
  1133. if (!eventsPool().pop_if_present(events))
  1134. events.reset(new Events());
  1135.  
  1136. // Submit the proxy task for scheduling.
  1137. job->tasks.push(boost::bind(&Implementation::task, events));
  1138. TaskScheduler::singleton().reschedule(job);
  1139.  
  1140. // Wait for the task to signal acquiredLock
  1141. events->acquiredLock.Wait();
  1142.  
  1143. if (job->taskType == DataModelJob::Write)
  1144. writeTransfer.reset(new DataModel::scoped_write_transfer(dataModel));
  1145.  
  1146. FASTLOG2(FLog::LegacyLock, "LegacyLock::Acquired type(%d), job (%s), events (%p)", job->taskType, events.get());
  1147. }
  1148. else
  1149. {
  1150. if (job->taskType == DataModelJob::Write)
  1151. RBXASSERT(dataModel->currentThreadHasWriteLock());
  1152.  
  1153. FASTLOG2(FLog::LegacyLock, "LegacyLock::RecursivelyAcquired DataModel: (%p), type(%d)", dataModel, job->taskType);
  1154. }
  1155. }
  1156.  
  1157. ~Implementation()
  1158. {
  1159. FASTLOG2(FLog::LegacyLock, "LegacyLock::~Implementation type(%d), events (%p)", job->taskType, events.get());
  1160.  
  1161. if (job != previousJob)
  1162. {
  1163. if (job->taskType == DataModelJob::Write)
  1164. writeTransfer.reset();
  1165.  
  1166. // The task has completed, so undo state
  1167. currentJob().reset(previousJob);
  1168. // Signal the proxy task that we're done
  1169. events->releasedLock.Set();
  1170. FASTLOG2(FLog::LegacyLock, "LegacyLock::Released DataModel: (%p), type(%d)", dataModel, job->taskType);
  1171. }
  1172. else
  1173. {
  1174. if (job->taskType == DataModelJob::Write)
  1175. RBXASSERT(dataModel->currentThreadHasWriteLock());
  1176.  
  1177. FASTLOG2(FLog::LegacyLock, "LegacyLock::RecursivelyReleased DataModel: (%p), type(%d)", dataModel, job->taskType);
  1178. }
  1179. }
  1180. };
  1181.  
  1182.  
  1183.  
  1184. DataModel::LegacyLock::Impersonator::Impersonator(shared_ptr<DataModel> dataModel, DataModelJob::TaskType taskType)
  1185. :previousJob(Implementation::currentJob().get())
  1186. {
  1187. Implementation::currentJob().reset(dataModel->getGenericJob(taskType).get());
  1188. }
  1189. DataModel::LegacyLock::Impersonator::~Impersonator()
  1190. {
  1191. Implementation::currentJob().reset(previousJob);
  1192. }
  1193.  
  1194. DataModel::LegacyLock::LegacyLock(boost::shared_ptr<DataModel> dataModel, DataModelJob::TaskType taskType)
  1195. // TODO: What if an invalid DataModelJob::Function is passed in???
  1196. :implementation(dataModel ? new Implementation(dataModel.get(), dataModel->getGenericJob(taskType)) : 0)
  1197. {
  1198. //assert(GetCurrentThreadId() != mainThreadId);
  1199. }
  1200.  
  1201. DataModel::LegacyLock::LegacyLock(DataModel* dataModel, DataModelJob::TaskType taskType)
  1202. :implementation(dataModel ? new Implementation(dataModel, dataModel->getGenericJob(taskType)) : 0)
  1203. {
  1204. //assert(GetCurrentThreadId() != mainThreadId);
  1205. }
  1206.  
  1207. DataModel::LegacyLock::~LegacyLock()
  1208. {
  1209. }
  1210.  
  1211. shared_ptr<DataModel::GenericJob> DataModel::tryGetGenericJob(DataModelJob::TaskType taskType)
  1212. {
  1213. RBX::mutex::scoped_lock lock(genericJobsLock);
  1214. return genericJobs[taskType];
  1215. }
  1216.  
  1217. shared_ptr<DataModel::GenericJob> DataModel::getGenericJob(DataModelJob::TaskType taskType)
  1218. {
  1219. shared_ptr<DataModel::GenericJob> job = tryGetGenericJob(taskType);
  1220. if (!job)
  1221. {
  1222. // This can also happen if the job is requested after the data model is closed.
  1223. throw std::runtime_error("Can't submit requested task type");
  1224. }
  1225. return job;
  1226. }
  1227.  
  1228. void DataModel::submitTask(Task task, DataModelJob::TaskType taskType)
  1229. {
  1230. if (shared_ptr<GenericJob> job = tryGetGenericJob(taskType))
  1231. {
  1232. job->tasks.push(task);
  1233. TaskScheduler::singleton().reschedule(job);
  1234. }
  1235. }
  1236.  
  1237. void DataModel::raiseClose()
  1238. {
  1239. closingSignal();
  1240. closingLateSignal();
  1241. }
  1242.  
  1243. void DataModel::traverseDataModelReporting(shared_ptr<Instance> node)
  1244. {
  1245. std::string className = node->getClassNameStr();
  1246. if(dataModelReportingData.find(className) == dataModelReportingData.end()){
  1247. dataModelReportingData[className] = 0;
  1248. }
  1249. dataModelReportingData[className]++;
  1250. }
  1251.  
  1252. void DataModel::setServerSaveUrl(std::string url)
  1253. {
  1254. serverSaveUrl = url;
  1255. }
  1256.  
  1257. bool DataModel::canSaveLocal() const
  1258. {
  1259. #ifdef _WIN32
  1260. return true;
  1261. #else
  1262. return false;
  1263. #endif
  1264. }
  1265.  
  1266. void DataModel::saveToRoblox(boost::function<void(bool)> resumeFunction, boost::function<void(std::string)> errorFunction)
  1267. {
  1268. if(canSave(this)){
  1269. if (Visit* visit = RBX::ServiceProvider::find<Visit>(this))
  1270. {
  1271. std::string uploadUrl = visit->getUploadUrl();
  1272. if(uploadUrl.find("http") == 0){
  1273. //needs to start with http
  1274. internalSaveAsync(ContentId(uploadUrl), resumeFunction);
  1275. return;
  1276. }
  1277. }
  1278. }
  1279.  
  1280. resumeFunction(false);
  1281. }
  1282.  
  1283. void DataModel::save(ContentId contentId)
  1284. {
  1285. if(canSave(this)){
  1286. internalSave(contentId);
  1287. }
  1288. saveFinishedSignal();
  1289. }
  1290.  
  1291.  
  1292. void DataModel::HttpHelper(std::string* response, std::exception* exception, boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction)
  1293. {
  1294. if(response){
  1295. resumeFunction(*response);
  1296. }
  1297. else{
  1298. errorFunction(exception->what());
  1299. }
  1300. }
  1301.  
  1302. void DataModel::doHttpGet(const std::string& url, boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction)
  1303. {
  1304. RBX::Http(url).get(boost::bind(&HttpHelper, _1, _2, resumeFunction, errorFunction));
  1305. }
  1306.  
  1307. std::string DataModel::doHttpGet(const std::string& url)
  1308. {
  1309. std::string response;
  1310. RBX::Http(url).get(response);
  1311. return response;
  1312. }
  1313.  
  1314. std::string DataModel::doHttpPost(const std::string& url, const std::string& data, const std::string& contentType)
  1315. {
  1316. std::string response;
  1317. std::istringstream stream(data);
  1318. Http(url).post(stream, contentType, data.size() > 256, response);
  1319. return response;
  1320. }
  1321.  
  1322. void DataModel::doHttpPost(const std::string& url, const std::string& data, const std::string& contentType, boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction)
  1323. {
  1324. Http(url).post(data, contentType, data.size() > 256,
  1325. boost::bind(&HttpHelper, _1, _2, resumeFunction, errorFunction));
  1326. }
  1327.  
  1328. static void httpResumeCallback(const std::string& response)
  1329. {
  1330. }
  1331.  
  1332. static void httpErrorCallback(const std::string& url, const std::string& error)
  1333. {
  1334. StandardOut::singleton()->printf(MESSAGE_ERROR, "%s: %s", url.c_str(), error.c_str());
  1335. }
  1336.  
  1337. std::string DataModel::httpGet(std::string url, bool synchronous)
  1338. {
  1339. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  1340. robloxScriptModifiedCheck(RBX::httpGetFunction.security);
  1341. if (url.size()>0)
  1342. {
  1343. if (synchronous)
  1344. {
  1345. return doHttpGet(url);
  1346. }
  1347. else
  1348. {
  1349. doHttpGet(url, httpResumeCallback, boost::bind(httpErrorCallback, url, _1));
  1350. }
  1351. }
  1352. return "";
  1353. }
  1354. void DataModel::httpGetAsync(std::string url, boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction)
  1355. {
  1356. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  1357. if (url.size() == 0)
  1358. {
  1359. errorFunction("Empty URL");
  1360. return;
  1361. }
  1362. robloxScriptModifiedCheck(RBX::httpGetAsyncFunction.security);
  1363. doHttpGet(url, resumeFunction, errorFunction);
  1364. }
  1365.  
  1366. void DataModel::reportMeasurement(std::string id, std::string key1, std::string value1, std::string key2, std::string value2)
  1367. {
  1368. ReportStatistic(GetBaseURL(), id, key1, value1, key2, value2);
  1369. }
  1370.  
  1371. std::string DataModel::httpPost(std::string url, std::string data, bool synchronous, std::string optionalContentType)
  1372. {
  1373. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  1374.  
  1375. robloxScriptModifiedCheck(RBX::httpPostFunction.security);
  1376. if (synchronous)
  1377. {
  1378. return doHttpPost(url, data, optionalContentType);
  1379. }
  1380. else
  1381. {
  1382. doHttpPost(url, data, optionalContentType, httpResumeCallback, boost::bind(httpErrorCallback, url, _1));
  1383. return "";
  1384. }
  1385. }
  1386.  
  1387. void DataModel::httpPostAsync(std::string url, std::string data, std::string optionalContentType, boost::function<void(std::string)> resumeFunction, boost::function<void(std::string)> errorFunction)
  1388. {
  1389. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  1390.  
  1391. if (url.size() == 0)
  1392. {
  1393. errorFunction("Empty URL");
  1394. return;
  1395. }
  1396.  
  1397. robloxScriptModifiedCheck(RBX::httpPostAsyncFunction.security);
  1398. doHttpPost(url, data, optionalContentType, resumeFunction, errorFunction);
  1399. }
  1400.  
  1401. void DataModel::luaReportGoogleAnalytics(std::string category, std::string action, std::string label, int value)
  1402. {
  1403. std::string encodedCategory = Http::urlEncode(category);
  1404. std::string encodedAction = Http::urlEncode(action);
  1405. std::string encodedLabel = Http::urlEncode(label);
  1406. RobloxGoogleAnalytics::trackEvent(encodedCategory.c_str(), encodedAction.c_str(), encodedLabel.c_str(), value);
  1407. }
  1408.  
  1409. std::auto_ptr<std::istream> DataModel::loadAssetIdIntoStream(int assetID)
  1410. {
  1411. // construct the url
  1412. std::string parameters = "asset/?id=" + boost::lexical_cast<std::string>(assetID);
  1413. std::string url = ServiceProvider::create<ContentProvider>(this)->getBaseUrl() + parameters;
  1414.  
  1415. StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "DataModel loading from: %s", url.c_str());
  1416.  
  1417. return std::auto_ptr<std::istream>(ServiceProvider::create<ContentProvider>(this)->getContent( ContentId(url) ));
  1418. }
  1419.  
  1420. void DataModel::loadWorld(int assetID)
  1421. {
  1422. if(!Network::Players::backendProcessing(this))
  1423. {
  1424. StandardOut::singleton()->printf(MESSAGE_WARNING, "Game:LoadWorld should only be called from a server script");
  1425. return;
  1426. }
  1427.  
  1428. std::auto_ptr<std::istream> stream = loadAssetIdIntoStream(assetID);
  1429.  
  1430. if(workspace)
  1431. {
  1432. workspace->clearTerrain();
  1433. workspace->removeAllChildren();
  1434. }
  1435.  
  1436. if (Lighting* lighting = ServiceProvider::find<Lighting>(this))
  1437. lighting->removeAllChildren();
  1438. if (Soundscape::SoundService* service = ServiceProvider::find<Soundscape::SoundService>(this))
  1439. service->removeAllChildren();
  1440. if (ServerStorage* service = ServiceProvider::find<ServerStorage>(this))
  1441. service->removeAllChildren();
  1442. if (ReplicatedStorage* service = ServiceProvider::find<ReplicatedStorage>(this))
  1443. service->removeAllChildren();
  1444. if (RobloxReplicatedStorage* service = ServiceProvider::find<RobloxReplicatedStorage>(this))
  1445. service->removeAllChildren();
  1446. if (ReplicatedFirst* service = ServiceProvider::find<ReplicatedFirst>(this))
  1447. service->removeAllChildren();
  1448.  
  1449. Serializer serializer;
  1450. serializer.load(*stream, this);
  1451. LuaSourceContainer::blockingLoadLinkedScripts(create<ContentProvider>(), this);
  1452.  
  1453. processAfterLoad();
  1454. }
  1455.  
  1456. void DataModel::loadGame(int assetID)
  1457. {
  1458. if(!Network::Players::backendProcessing(this))
  1459. {
  1460. StandardOut::singleton()->printf(MESSAGE_WARNING, "Game:LoadWorld should only be called from a server script");
  1461. return;
  1462. }
  1463.  
  1464. std::auto_ptr<std::istream> stream = loadAssetIdIntoStream(assetID);
  1465.  
  1466. if (ServerScriptService* service = ServiceProvider::find<ServerScriptService>(this))
  1467. service->removeAllChildren();
  1468. if (starterGuiService)
  1469. starterGuiService->removeAllChildren();
  1470. if (starterPackService)
  1471. starterPackService->removeAllChildren();
  1472. if (starterPlayerService)
  1473. starterPlayerService->removeAllChildren();
  1474.  
  1475. Serializer serializer;
  1476. serializer.load(*stream, this);
  1477. LuaSourceContainer::blockingLoadLinkedScripts(create<ContentProvider>(), this);
  1478. }
  1479.  
  1480. void DataModel::loadContent(ContentId contentId)
  1481. {
  1482. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  1483.  
  1484. if (isContentLoaded)
  1485. {
  1486. Analytics::GoogleAnalytics::trackEventWithoutThrottling(GA_CATEGORY_ERROR, "loadContent re-entrant", contentId.getAssetId().c_str());
  1487. return;
  1488. }
  1489.  
  1490. StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "DataModel Loading %s", contentId.c_str());
  1491.  
  1492. G3D::RealTime t1 = G3D::System::time(); // time in seconds
  1493. std::auto_ptr<std::istream> stream(ServiceProvider::create<ContentProvider>(this)->getContent(contentId, "Place"));
  1494. G3D::RealTime t2 = G3D::System::time();
  1495.  
  1496. // post-load check on RCC. An exploit that can overwrite pointers on RCC could jump to the
  1497. // middle of this function.
  1498. if (isContentLoaded)
  1499. {
  1500. stream.release();
  1501. Analytics::GoogleAnalytics::trackEventWithoutThrottling(GA_CATEGORY_ERROR, "loadContent re-entrant post", contentId.getAssetId().c_str());
  1502. return;
  1503. }
  1504.  
  1505. Serializer serializer;
  1506. serializer.load(*stream, this);
  1507. LuaSourceContainer::blockingLoadLinkedScripts(create<ContentProvider>(), this);
  1508.  
  1509. G3D::RealTime t3 = G3D::System::time();
  1510. processAfterLoad();
  1511. G3D::RealTime t4 = G3D::System::time();
  1512.  
  1513. workspace->setStatsSyncHttpGetTime(t2 - t1);
  1514. workspace->setStatsXMLLoadTime(t3 - t2);
  1515. workspace->setStatsJoinAllTime(t4 - t3);
  1516.  
  1517. workspace->createTerrain();
  1518. workspaceLoadedSignal();
  1519.  
  1520. isContentLoaded = true; // there should be a better way to do this.
  1521.  
  1522. }
  1523.  
  1524. void DataModel::processAfterLoad()
  1525. {
  1526. workspace->joinAllHack();
  1527.  
  1528. if (DFFlag::MaterialPropertiesEnabled)
  1529. {
  1530. // In case we have decided to make New Physical Properties the default,
  1531. // we want to migrate parts by default.
  1532. if (workspace->getUsingNewPhysicalProperties())
  1533. {
  1534. RBX::PartInstance::convertToNewPhysicalPropRecursive(this);
  1535. }
  1536. }
  1537. }
  1538.  
  1539. #if defined(RBX_STUDIO_BUILD) || defined(RBX_RCC_SECURITY) || defined(RBX_TEST_BUILD)
  1540. shared_ptr<const Instances> DataModel::fetchAsset(ContentId contentId)
  1541. {
  1542. RBXASSERT(isInitialized); // If hit show to David or Erik - threading issue
  1543.  
  1544. ContentProvider* cp = create<ContentProvider>();
  1545. std::auto_ptr<std::istream> stream(cp->getContent(contentId));
  1546.  
  1547. // Return all the new items in a table
  1548. shared_ptr<Instances> result(new Instances());
  1549.  
  1550. // Read the items
  1551. Serializer().loadInstances(*stream, *result);
  1552. LuaSourceContainer::blockingLoadLinkedScriptsForInstances(cp, *result);
  1553.  
  1554. return result;
  1555. }
  1556. #endif
  1557.  
  1558. void DataModel::onChildAdded(Instance* child)
  1559. {
  1560. Super::onChildAdded(child);
  1561.  
  1562. if (Network::Players::isNetworkClient(child))
  1563. {
  1564. // TODO: Hack
  1565. // For now, this hook sets the mouse command to the NULL command, regardless of whether there is a player/character or not
  1566. // This prevents the arrow tool from coming up on a multiplayer load
  1567. workspace->setNullMouseCommand();
  1568. }
  1569. }
  1570.  
  1571.  
  1572. bool DataModel::askAddChild(const Instance* instance) const
  1573. {
  1574. return dynamic_cast<const Service*>(instance)!=NULL;
  1575. }
  1576.  
  1577.  
  1578. double DataModel::getGameTime() const
  1579. {
  1580. RBXASSERT(runService);
  1581. return runService->gameTime();
  1582. }
  1583.  
  1584.  
  1585.  
  1586. double DataModel::getSmoothFps() const
  1587. {
  1588. RBXASSERT(runService);
  1589. return runService->smoothFps();
  1590. }
  1591.  
  1592.  
  1593. ////////////////////////////////////////////////////////////////////////////////////
  1594. //
  1595. // Rendering
  1596. //
  1597.  
  1598. void DataModel::computeGuiInset(Adorn* adorn)
  1599. {
  1600.  
  1601. Vector4 guiInset;
  1602. if (RBX::GuiService* guiService = RBX::ServiceProvider::find<RBX::GuiService>(this))
  1603. {
  1604. guiInset = guiService->getGlobalGuiInset();
  1605. }
  1606. else
  1607. {
  1608. guiInset = Vector4(0, 0, 0, 0);
  1609. }
  1610.  
  1611. adorn->setUserGuiInset(guiInset);
  1612. }
  1613.  
  1614. void DataModel::renderPlayerGui(Adorn* adorn)
  1615. {
  1616. RBX::Network::Player* player = RBX::Network::Players::findLocalPlayer(this);
  1617. if ( !Network::Players::isCloudEdit(this) && player )
  1618. {
  1619. for (size_t i = 0; i < player->numChildren(); i++)
  1620. {
  1621. Instance *child = player->getChild(i);
  1622. if (PlayerGui* playergui = Instance::fastDynamicCast<PlayerGui>(child))
  1623. playergui->render2d(adorn);
  1624. }
  1625. }
  1626. else
  1627. starterGuiService->render2d(adorn);
  1628. }
  1629.  
  1630. void DataModel::renderGuiRoot(Adorn* adorn)
  1631. {
  1632. guiBuilder.updateGui();
  1633. guiRoot->render2d(adorn);
  1634. }
  1635.  
  1636. std::string DataModel::getUpdatedMessageBoxText()
  1637. {
  1638. std::string printMessage = "";
  1639. if (uiMessage == "[[[progress]]]")
  1640. {
  1641. printMessage = "Loading... Instances: "
  1642. + getMetric("numInstances")
  1643. + " Bricks: "
  1644. + getMetric("numPrimitives")
  1645. + " Connectors: "
  1646. + getMetric("numJoints");
  1647.  
  1648. MegaClusterInstance* terrain = Instance::fastDynamicCast<MegaClusterInstance>(
  1649. workspace->getTerrain());
  1650. unsigned int cellCount = 0;
  1651. if (terrain) {
  1652. cellCount = terrain->getNonEmptyCellCount();
  1653. }
  1654. if (cellCount > 0) {
  1655. std::string result(15, '\0');
  1656. snprintf(&result[0], 15, "%.3f mil", cellCount / 1000000.0f);
  1657.  
  1658. printMessage += " Voxels: " + result;
  1659. }
  1660. }
  1661. else if(uiMessage != "[[[progress]]]")
  1662. printMessage = uiMessage;
  1663.  
  1664. return printMessage;
  1665. }
  1666.  
  1667. bool DataModel::canRenderMouse()
  1668. {
  1669. if (userInputService->getLastInputType() == InputObject::TYPE_TOUCH)
  1670. {
  1671. return false;
  1672. }
  1673.  
  1674. if (userInputService->getOverrideMouseIconBehavior() == UserInputService::OVERRIDE_BEHAVIOR_FORCEHIDE)
  1675. {
  1676. return false;
  1677. }
  1678. else if (userInputService->getOverrideMouseIconBehavior() == UserInputService::OVERRIDE_BEHAVIOR_FORCESHOW)
  1679. {
  1680. return true;
  1681. }
  1682.  
  1683. if(!userInputService->getMouseIconEnabled())
  1684. {
  1685. return false;
  1686. }
  1687.  
  1688. bool playerExists = (RBX::Network::Players::findLocalPlayer(this) != NULL) && !Network::Players::isCloudEdit(this);
  1689.  
  1690. if ( UserInputService::isTenFootInterface() )
  1691. {
  1692. return userInputService->getMouseIconEnabled();
  1693. }
  1694.  
  1695. return (!RBX::MouseCommand::isAdvArrowToolEnabled() || playerExists) && userInputService->getMouseEnabled();
  1696. }
  1697.  
  1698. void DataModel::renderPass2d(Adorn* adorn, IMetric* graphicsMetric)
  1699. {
  1700. RBXPROFILER_SCOPE("Render", "Pass2d");
  1701.  
  1702. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  1703.  
  1704. if (!isInitialized)
  1705. return;
  1706. if (!adorn)
  1707. return;
  1708. if (!graphicsMetric)
  1709. return;
  1710.  
  1711. tempMetric = graphicsMetric;
  1712.  
  1713. //compute header/footer to restrict the Gui (used for MFC chat, todo:remove this when we have lua chat)
  1714. computeGuiInset(adorn);
  1715.  
  1716. // 1. Render 2D world items (names, cursors, ScreenGuis, user items in workspace, etc.)
  1717. workspace->render2d(adorn);
  1718.  
  1719. if (!DFFlag::AllowHideHudShortcut || renderGuisActive)
  1720. {
  1721. // 2. Render the User created gui - either inside the PlayerGui, or the StarterGui
  1722. renderPlayerGui(adorn);
  1723.  
  1724. // 3. Render the ROBLOX gui elements
  1725. coreGuiService->render2d(adorn);
  1726.  
  1727. // 4. Render old school guiRoot (don't add anything to this, only debug stats are shown here: Shift-F1 and Shift-F2)
  1728. renderGuiRoot(adorn);
  1729.  
  1730. // 5. Finally, render the mouse
  1731. renderMouse(adorn);
  1732. }
  1733.  
  1734. tempMetric = NULL; // be safe, never use again unless set
  1735. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  1736. }
  1737.  
  1738. ContentId DataModel::getRenderMouseCursor()
  1739. {
  1740. if (forceArrowCursor)
  1741. {
  1742. if (!MouseCommand::isAdvArrowToolEnabled() ||
  1743. (RBX::Network::Players::findLocalPlayer(this) || Network::Players::serverIsPresent(this)))
  1744. {
  1745. return userInputService->getDefaultMouseCursor(true);
  1746. }
  1747. else
  1748. {
  1749. return userInputService->getDefaultMouseCursor(true);
  1750. }
  1751. }
  1752. else
  1753. {
  1754. ContentId workspaceCursor = workspace->getCursor();
  1755. if (workspaceCursor.isNull())
  1756. {
  1757. return userInputService->getDefaultMouseCursor(true);
  1758. }
  1759. return workspaceCursor;
  1760. }
  1761. }
  1762.  
  1763. void DataModel::renderMouse(Adorn* adorn)
  1764. {
  1765. if (Profiler::isCapturingMouseInput())
  1766. return;
  1767.  
  1768. ControllerService* service = ServiceProvider::create<ControllerService>(this);
  1769. if(!service)
  1770. return;
  1771.  
  1772. if (UserInputService::IsUsingNewKeyboardEvents())
  1773. {
  1774. // don't need userinputbase for SDL
  1775. }
  1776. else
  1777. {
  1778. #ifndef RBX_PLATFORM_UWP
  1779. UserInputBase* hardwareDevice = service->getHardwareDevice();
  1780. if (hardwareDevice)
  1781. {
  1782. hardwareDevice->setCursorId(adorn, getRenderMouseCursor());
  1783. #endif
  1784. }
  1785. }
  1786.  
  1787. if (!canRenderMouse())
  1788. {
  1789. return;
  1790. }
  1791.  
  1792. bool waiting = false;
  1793.  
  1794. // lazy connect to signal
  1795. if (!unbindResourceSignal.connected())
  1796. {
  1797. unbindResourceSignal = adorn->getUnbindResourcesSignal().connect(boost::bind(&DataModel::onUnbindResourceSignal, this));
  1798. }
  1799.  
  1800. renderCursor = adorn->createTextureProxy(getRenderMouseCursor(), waiting, false, "Mouse Cursor");
  1801. if (!renderCursor && userInputService)
  1802. {
  1803. renderCursor = adorn->createTextureProxy(userInputService->getDefaultMouseCursor(false), waiting);
  1804. }
  1805.  
  1806. if (renderCursor && userInputService->getCurrentMousePosition())
  1807. {
  1808. Rect2D rect = adorn->getTextureSize(renderCursor);
  1809.  
  1810. // Center the texture on the cursor position
  1811. rect = rect - rect.center() + userInputService->getCurrentMousePosition()->get2DPosition();
  1812.  
  1813. rect = Rect2D::xyxy( Math::roundVector2(rect.x0y0()), Math::roundVector2(rect.x1y1()) );
  1814.  
  1815. adorn->setTexture(0, renderCursor);
  1816. adorn->rect2d(rect, G3D::Color4(1,1,1,1));
  1817. adorn->setTexture(0, TextureProxyBaseRef());
  1818. }
  1819. }
  1820.  
  1821. void DataModel::onUnbindResourceSignal()
  1822. {
  1823. renderCursor.reset();
  1824. unbindResourceSignal.disconnect();
  1825. }
  1826.  
  1827. void DataModel::renderPass3dAdorn(Adorn* adorn)
  1828. {
  1829. RBXPROFILER_SCOPE("Render", "Pass3dAdorn");
  1830.  
  1831. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  1832. if (!isInitialized) {
  1833. return;
  1834. }
  1835.  
  1836. // a couple of local variables for the ctrl-F1 hud
  1837. drawId++;
  1838.  
  1839. //
  1840. if (!adorn) {
  1841. RBXASSERT(0);
  1842. return;
  1843. }
  1844.  
  1845. std::vector<AdornableDepth> sortedAdorn;
  1846. workspace->render3dAdorn(adorn);
  1847. workspace->append3dSortedAdorn(sortedAdorn);
  1848.  
  1849. RBX::Network::Players* players = ServiceProvider::create<RBX::Network::Players>(this);
  1850. if (RBX::Network::Player* player = players->getLocalPlayer()) {
  1851. if (PlayerGui* playerGui = player->findFirstChildOfType<PlayerGui>()) {
  1852. playerGui->render3dAdorn(adorn);
  1853. playerGui->append3dSortedAdorn(sortedAdorn, workspace->getConstCamera());
  1854. }
  1855. }
  1856. else{
  1857. starterGuiService->render3dAdorn(adorn);
  1858. starterGuiService->append3dSortedAdorn(sortedAdorn, workspace->getConstCamera());
  1859. }
  1860.  
  1861. coreGuiService->render3dAdorn(adorn);
  1862. coreGuiService->append3dSortedAdorn(sortedAdorn, workspace->getConstCamera());
  1863.  
  1864. std::sort(sortedAdorn.begin(), sortedAdorn.end());
  1865.  
  1866. FASTLOG1(FLog::AdornRenderStats, "Rendering 3D Sorted Adorn Items, %u items", sortedAdorn.size());
  1867.  
  1868. for (auto& ad: sortedAdorn)
  1869. ad.adornable->render3dSortedAdorn(adorn);
  1870.  
  1871. if (!forceArrowCursor) {
  1872. workspace->getCurrentMouseCommand()->render3dAdorn(adorn);
  1873. }
  1874.  
  1875. if (Workspace::showEPhysicsRegions) {
  1876. ServiceProvider::create<RBX::Network::Players>(this)->renderDPhysicsRegions(adorn);
  1877. }
  1878.  
  1879. if (Workspace::showStreamedRegions)
  1880. {
  1881. adorn->setObjectToWorldMatrix(CoordinateFrame());
  1882. if (RBX::Network::Player* player = players->getLocalPlayer())
  1883. player->renderStreamedRegion(adorn);
  1884. }
  1885.  
  1886. if (Workspace::showPartMovementPath)
  1887. {
  1888. adorn->setObjectToWorldMatrix(CoordinateFrame());
  1889. if (RBX::Network::Player* player = players->getLocalPlayer())
  1890. player->renderPartMovementPath(adorn);
  1891. }
  1892.  
  1893. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  1894. }
  1895.  
  1896. //Updates non-reflected state for camera and whatnot
  1897. void DataModel::renderStep(float timeIntervalSeconds)
  1898. {
  1899. RBXPROFILER_SCOPE("Render", "Step");
  1900.  
  1901. if (runService)
  1902. {
  1903. runService->earlyRenderSignal();
  1904. }
  1905.  
  1906. RBXASSERT(workspace);
  1907. if (FFlag::RelativisticCameraFixEnable && workspace)
  1908. {
  1909. if (Camera* camera = workspace->getCamera())
  1910. {
  1911. // Relativistic Bug (DE9363):
  1912. //
  1913. // Camera was being updated after the UI processing occurred, however the SurfaceGUI processing (which is part of the UI pipeline) required
  1914. // updated camera to perform raycasts. This resulted in a bug where the raycast would use the camera from previous frame - not good if the
  1915. // camera was attached to a moving object with SurfaceGUIs on it.
  1916. //
  1917. // The correct way would be to split out the UI processing that modifies the camera (e.g. movement, panning) into a separate call and do that before
  1918. // the camera update, and perform the rest afterwards.
  1919. //
  1920. // So this ugly hack will hang along for some indeterminate amount of time... Or perhaps for eternity.
  1921.  
  1922. camera->step(timeIntervalSeconds);
  1923. userInputService->onRenderStep();
  1924. camera->step(0);
  1925. }
  1926. }
  1927. else
  1928. {
  1929. userInputService->onRenderStep();
  1930. }
  1931.  
  1932. if (runService)
  1933. {
  1934. runService->renderStepped(timeIntervalSeconds, false);
  1935. }
  1936.  
  1937. // Need to step subject update after lua controls
  1938. if (Camera* camera = workspace->getCamera())
  1939. {
  1940. camera->stepSubject();
  1941. }
  1942. }
  1943.  
  1944.  
  1945. float DataModel::physicsStep(float timeInterval, double dt, double dutyDt, int numThreads)
  1946. {
  1947. if (!isInitialized) {
  1948. RBXASSERT(0);
  1949. return 0.0f; // 12/05/07 - possibly seeing this being called during destruction in other thread?
  1950. }
  1951.  
  1952. // On Mac there is nothing equivalent of _CrtCheckMemory to validate the state of the heap
  1953. // Instead use this on XCode http://developer.apple.com/library/ios/#documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html
  1954. #ifdef _WIN32
  1955. RBXASSERT_IF_VALIDATING(_CrtCheckMemory());
  1956. #endif
  1957. bool isCyclicExecutive = RBX::TaskScheduler::singleton().isCyclicExecutive();
  1958. float dtCyclicExecutive = 0.0f;
  1959. if( isCyclicExecutive )
  1960. {
  1961. int nSteps = workspace->updatePhysicsStepsRequiredForCyclicExecutive(dt);
  1962. if( nSteps == 0 && DataModel::throttleAt30Fps)
  1963. {
  1964. return 0.0f;
  1965. }
  1966.  
  1967. dtCyclicExecutive = (float)nSteps * Constants::worldDt();
  1968. }
  1969.  
  1970. // TaskSchedulerCyclicExecutive: This should simply be triggered after every
  1971. // 1/30s. Not changing it just yet.
  1972.  
  1973. bool longStep = physicsStepID % 2 == 0;
  1974.  
  1975. Profiling::Mark mark(*workspace->profileDataModelStep, true, true);
  1976.  
  1977. float timeIntervalUsed = 0.0f;
  1978.  
  1979. // Physics Step
  1980. RBX::Network::GameMode gameMode = Network::Players::getGameMode(this);
  1981. if (updatePhysicsInstructions(gameMode))
  1982. {
  1983. if ( (gameMode == Network::DPHYS_CLIENT || gameMode == Network::CLIENT || gameMode == Network::VISIT_SOLO ) && !isCyclicExecutive )
  1984. {
  1985. runService->gameStepped( Constants::uiDt(), longStep);
  1986. }
  1987. else if (longStep && !isCyclicExecutive)
  1988. {
  1989. runService->gameStepped( Constants::longUiStepDt(), true);
  1990. }
  1991. else if (isCyclicExecutive)
  1992. {
  1993. runService->gameStepped(dtCyclicExecutive, true);
  1994. }
  1995.  
  1996. {
  1997. RBX::Profiling::Mark mark2(*workspace->profileWorkspaceAssemble, true);
  1998. workspace->assemble(); // must do assembly here, because "handle fallen parts" may disassemble. Also, runService->gameStepped may disassemble
  1999. }
  2000. {
  2001. Profiling::Mark mark2(*workspace->profileWorkspaceStep, true, true);
  2002.  
  2003. Network::Player* dPhysPlayer = (gameMode == Network::DPHYS_CLIENT) ? Network::Players::findLocalPlayer(this) : NULL;
  2004.  
  2005. FASTLOG3F(FLog::CyclicExecutiveThrottling, "DataModel::PhysicsStep dt: %f, dtCyclicExecutive: %f, dutyDt: %f", dt, dtCyclicExecutive, dutyDt);
  2006. if (isCyclicExecutive)
  2007. {
  2008. physicsInstructions.setCyclicThrottles(dPhysPlayer, workspace.get(), dtCyclicExecutive, dt, dutyDt);
  2009. }
  2010. else
  2011. {
  2012. physicsInstructions.setThrottles(dPhysPlayer, workspace.get(), dt, dutyDt);
  2013. }
  2014. timeIntervalUsed = workspace->physicsStep(longStep, timeInterval, numThreads);
  2015. FASTLOG1F(FLog::CyclicExecutiveTiming, "DataModel::physicsStepped: %4.7f", timeIntervalUsed);
  2016. }
  2017. }
  2018. else
  2019. {
  2020. // Assemble always
  2021. RBX::Profiling::Mark mark2(*workspace->profileWorkspaceAssemble, true);
  2022. workspace->assemble();
  2023. }
  2024.  
  2025. if (gameMode == RBX::Network::DPHYS_GAME_SERVER && (physicsStepID % 4) == 0)
  2026. {
  2027. // only server records the movement history, once every 3 physics steps
  2028. workspace->updateHistory();
  2029. }
  2030.  
  2031. ++physicsStepID;
  2032.  
  2033. // On Mac there is nothing equivalent of _CrtCheckMemory to validate the state of the heap
  2034. // Instead use this on XCode http://developer.apple.com/library/ios/#documentation/Performance/Conceptual/ManagingMemory/Articles/MallocDebug.html
  2035. #ifdef _WIN32
  2036. RBXASSERT_IF_VALIDATING(_CrtCheckMemory());
  2037. #endif
  2038.  
  2039. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  2040.  
  2041. return timeIntervalUsed;
  2042. }
  2043.  
  2044.  
  2045.  
  2046.  
  2047. // TODO: make these objects
  2048. bool DataModel::updatePhysicsInstructions(Network::GameMode gameMode)
  2049. {
  2050. SimSendFilter& simSendFilter = workspace->getWorld()->getSimSendFilter();
  2051. simSendFilter.networkAddress = RBX::Network::Players::findLocalSimulatorAddress(this);
  2052. physicsInstructions.requestedDutyPercent = 0.0;
  2053. physicsInstructions.bandwidthExceeded = false;
  2054.  
  2055. switch (gameMode)
  2056. {
  2057. case Network::VISIT_SOLO:
  2058. case Network::EDIT:
  2059. {
  2060. RBXASSERT(simSendFilter.networkAddress == Network::NetworkOwner::Unassigned());
  2061. simSendFilter.mode = SimSendFilter::EditVisit;
  2062. physicsInstructions.requestedDutyPercent = PhysicsInstructions::visitSoloDutyPercent();
  2063. return (runService->getRunState() == RS_RUNNING);
  2064. }
  2065. case Network::GAME_SERVER:
  2066. {
  2067. RBXASSERT(simSendFilter.networkAddress == Network::NetworkOwner::Unassigned());
  2068. simSendFilter.mode = SimSendFilter::Server;
  2069. physicsInstructions.requestedDutyPercent = PhysicsInstructions::regularServerDutyPercent();
  2070. return (runService->getRunState() == RS_RUNNING);
  2071. }
  2072. case Network::DPHYS_GAME_SERVER:
  2073. {
  2074. RBXASSERT(simSendFilter.networkAddress == Network::NetworkOwner::Server());
  2075. simSendFilter.mode = SimSendFilter::dPhysServer;
  2076. simSendFilter.region.clearEmpty();
  2077. physicsInstructions.requestedDutyPercent = PhysicsInstructions::dPhysicsServerDutyPercent();
  2078. return (runService->getRunState() == RS_RUNNING);
  2079. }
  2080. case Network::CLIENT:
  2081. case Network::WATCH_ONLINE:
  2082. {
  2083. RBXASSERT(simSendFilter.networkAddress == Network::NetworkOwner::Unassigned());
  2084. simSendFilter.mode = SimSendFilter::Client;
  2085. physicsInstructions.requestedDutyPercent = PhysicsInstructions::zeroDutyPercent();
  2086. return false;
  2087. }
  2088. case Network::DPHYS_CLIENT:
  2089. {
  2090. RBXASSERT(Network::Players::clientIsPresent(this, true));
  2091. simSendFilter.mode = SimSendFilter::dPhysClient;
  2092. ServiceProvider::create<RBX::Network::Players>(this)->buildClientRegion(simSendFilter.region);
  2093. physicsInstructions.requestedDutyPercent = PhysicsInstructions::dPhysicsClientEThrottleDutyPercent();
  2094.  
  2095. physicsInstructions.bandwidthExceeded = Network::Player::physicsOutBandwidthExceeded(this);
  2096. physicsInstructions.networkBufferHealth = Network::Player::getNetworkBufferHealth(this);
  2097. return true;
  2098. }
  2099. default:
  2100. {
  2101. RBXASSERT(0);
  2102. return false;
  2103. }
  2104. }
  2105. }
  2106.  
  2107. void DataModel::checkFetchExperimentalFeatures()
  2108. {
  2109. if(getPlaceID() == 0)
  2110. {
  2111. FASTLOG(FLog::CloseDataModel, "Place Id is zero, skipping fetch");
  2112. return;
  2113. }
  2114.  
  2115. if(checkedExperimentalFeatures)
  2116. return;
  2117.  
  2118. ContentProvider* cp = find<ContentProvider>();
  2119. if(!cp)
  2120. {
  2121. RBXASSERT(false);
  2122. return;
  2123. }
  2124.  
  2125. std::string response;
  2126.  
  2127. if (RBX::HttpRbxApiService* apiService = RBX::ServiceProvider::find<RBX::HttpRbxApiService>(this))
  2128. {
  2129. apiService->get(RBX::format("game/GetAllowedExperimentalFeatures?placeId=%i", getPlaceID()), true, PRIORITY_EXTREME, response);
  2130. }
  2131.  
  2132. std::stringstream rawStream(response);
  2133. shared_ptr<const Reflection::ValueTable> result;
  2134. if(!WebParser::parseJSONTable(response, result))
  2135. {
  2136. FASTLOG(FLog::CloseDataModel, "Can't parse JSON");
  2137. RBXASSERT(false);
  2138. checkedExperimentalFeatures = true;
  2139. return;
  2140. }
  2141.  
  2142. checkedExperimentalFeatures = true;
  2143. }
  2144.  
  2145. void DataModel::onRunTransition(RunTransition event)
  2146. {
  2147. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  2148. if (!isInitialized) {
  2149. return;
  2150. }
  2151.  
  2152. switch (event.newState)
  2153. {
  2154. case RS_STOPPED:
  2155. // only compiles in if we have a timer
  2156.  
  2157. if (event.oldState == RS_RUNNING) {
  2158. workspace->reset();
  2159. }
  2160. break;
  2161.  
  2162. case RS_RUNNING:
  2163. workspace->start();
  2164. break;
  2165.  
  2166. case RS_PAUSED:
  2167. workspace->stop();
  2168. break;
  2169. }
  2170. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  2171. }
  2172.  
  2173. ///////////////////////////////////////////////////////////////////
  2174. // inquiry....
  2175.  
  2176. static void notificationCallbackError(std::string error)
  2177. {
  2178. }
  2179.  
  2180. static void notificationCallbackSuccess(DataModel* dm)
  2181. {
  2182. dm->setRenderGuisActive(false);
  2183. }
  2184.  
  2185. GuiResponse DataModel::processAccelerators(const shared_ptr<InputObject>& event) // true if used the event
  2186. {
  2187. Verb* verb = NULL;
  2188. bool preventSinking = false;
  2189.  
  2190. if (networkStatsWindowsOn)
  2191. {
  2192. switch (event->getUserInputType())
  2193. {
  2194. case InputObject::TYPE_KEYBOARD:
  2195. {
  2196. // we only want key down events
  2197. if(!event->isKeyDownEvent())
  2198. break;
  2199.  
  2200. switch (event->getKeyCode())
  2201. {
  2202. case SDLK_1:
  2203. if (event->isKeyPressedWithShiftEvent(event->getKeyCode()))
  2204. guiBuilder.nextNetworkStats();
  2205. break;
  2206. default:
  2207. break;
  2208. }
  2209. }
  2210. default:
  2211. break;
  2212. }
  2213. }
  2214.  
  2215. switch (event->getUserInputType())
  2216. {
  2217. case InputObject::TYPE_KEYBOARD:
  2218. {
  2219. RBXASSERT(event->isKeyDownEvent() || event->isKeyUpEvent());
  2220.  
  2221. if(event->isKeyUpEvent())
  2222. {
  2223. switch(event->getKeyCode())
  2224. {
  2225. case SDLK_LALT:
  2226. case SDLK_RALT:
  2227. RBX::GameBasicSettings::singleton().setFreeLook(false);
  2228. break;
  2229. default:
  2230. RBX::GameBasicSettings::singleton().setFreeLook(false);
  2231. break;
  2232. }
  2233. break;
  2234. }
  2235.  
  2236. // if not keydown at this point, bail out
  2237. if(!event->isKeyDownEvent())
  2238. break;
  2239.  
  2240. {
  2241. if (ServiceProvider::find<GuiService>()->processKeyDown(event))
  2242. {
  2243. return GuiResponse::sunk();
  2244. }
  2245. else
  2246. {
  2247. bool isCustomCameraType = false;
  2248. if (const Camera* camera = workspace->getConstCamera())
  2249. {
  2250. isCustomCameraType = (camera->getCameraType() == Camera::CUSTOM_CAMERA);
  2251. }
  2252. bool shouldDoCameraVerbs = !isCustomCameraType;
  2253. if (const Camera* camera = workspace->getConstCamera())
  2254. {
  2255. if (FFlag::UserAllCamerasInLua && camera->hasClientPlayer())
  2256. {
  2257. shouldDoCameraVerbs = false;
  2258. }
  2259. }
  2260.  
  2261. switch (event->getKeyCode())
  2262. {
  2263. case SDLK_i:
  2264. if (shouldDoCameraVerbs)
  2265. {
  2266. verb = workspace->getWhitelistVerb("Camera", "Zoom", "In");
  2267. }
  2268. break;
  2269. case SDLK_o:
  2270. if (shouldDoCameraVerbs)
  2271. {
  2272. verb = workspace->getWhitelistVerb("Camera", "Zoom", "Out");
  2273. }
  2274. break;
  2275. case SDLK_COMMA:
  2276. // Camera panning is hard-coded to be always enabled, so we just do a quick check before we get a camera panning verb, so this doesn't break the Scriptable Camera
  2277. if (shouldDoCameraVerbs && workspace->getConstCamera()->getCameraType() != Camera::LOCKED_CAMERA)
  2278. {
  2279. verb = workspace->getWhitelistVerb("Camera", "Pan", "Left");
  2280. }
  2281. break;
  2282. case SDLK_PERIOD:
  2283. if (shouldDoCameraVerbs && workspace->getConstCamera()->getCameraType() != Camera::LOCKED_CAMERA)
  2284. {
  2285. verb = workspace->getWhitelistVerb("Camera", "Pan", "Right");
  2286. }
  2287. break;
  2288. case SDLK_PAGEUP:
  2289. if (shouldDoCameraVerbs)
  2290. {
  2291. verb = workspace->getWhitelistVerb("Camera", "Tilt", "Up");
  2292. }
  2293. break;
  2294. case SDLK_PAGEDOWN:
  2295. if (shouldDoCameraVerbs)
  2296. {
  2297. verb = workspace->getWhitelistVerb("Camera", "Tilt", "Down");
  2298. }
  2299. break;
  2300. case SDLK_BACKSPACE:
  2301. {
  2302. if (RBX::Network::Players* players = ServiceProvider::create<RBX::Network::Players>(this))
  2303. {
  2304. if (RBX::Network::Player* player = players->getLocalPlayer())
  2305. {
  2306. Tool::dropAll(player);
  2307. }
  2308. }
  2309. break;
  2310. }
  2311.  
  2312. case SDLK_EQUALS:
  2313. {
  2314. if (ModelInstance* character = Network::Players::findLocalCharacter(this))
  2315. {
  2316. Accoutrement::dropAll(character);
  2317. }
  2318. break;
  2319. }
  2320.  
  2321. /**************************************/
  2322.  
  2323. case SDLK_F1:
  2324. if ( !RBX::GameBasicSettings::singleton().inStudioMode() )
  2325. {
  2326. if (event->mod==0)
  2327. verb = getWhitelistVerb("H", "el", "p");
  2328. else
  2329. verb = getWhitelistVerb("S", "ta", "ts");
  2330. }
  2331. break;
  2332.  
  2333. case SDLK_F2:
  2334. if ( !RBX::GameBasicSettings::singleton().inStudioMode() && event->isKeyPressedWithShiftEvent(event->getKeyCode()) )
  2335. verb = getWhitelistVerb("", "Render", "Stats");
  2336. /*else
  2337. verb = getWhitelistVerb("TogglePlayMode");*/
  2338. // TODO: turn on above statement when f2 studio hack no longer is an issue
  2339.  
  2340. break;
  2341. case SDLK_F3:
  2342. if ( !RBX::GameBasicSettings::singleton().inStudioMode() && event->isKeyPressedWithShiftEvent(event->getKeyCode()) )
  2343. verb = getWhitelistVerb("Network", "", "Stats");
  2344. break;
  2345.  
  2346. case SDLK_F4:
  2347. if ( !RBX::GameBasicSettings::singleton().inStudioMode() && event->isKeyPressedWithShiftEvent(event->getKeyCode()) )
  2348. verb = getWhitelistVerb("Physics", "Stats", "");
  2349. break;
  2350.  
  2351. case SDLK_F5: // TODO: use for run command in studio
  2352. if ( !RBX::GameBasicSettings::singleton().inStudioMode() && event->isKeyPressedWithShiftEvent(event->getKeyCode()) )
  2353. verb = getWhitelistVerb("Summary", "" , "Stats");
  2354. break;
  2355.  
  2356. case SDLK_F6:
  2357. if ( !RBX::GameBasicSettings::singleton().inStudioMode() && event->isKeyPressedWithShiftEvent(event->getKeyCode()) )
  2358. verb = getWhitelistVerb("Custom", "Stats", "");
  2359. break;
  2360. case SDLK_F7:
  2361. if (DFFlag::AllowHideHudShortcut && !RBX::GameBasicSettings::singleton().getUsedHideHudShortcut())
  2362. {
  2363. if (RBX::GuiService* guiService = RBX::ServiceProvider::find<RBX::GuiService>(this))
  2364. {
  2365. if (!guiService->notificationCallback.empty())
  2366. {
  2367. RBX::GameBasicSettings::singleton().setUsedHideHudShortcut(true);
  2368. guiService->notificationCallback("HUD Hidden", "Press F7 again when you want to unhide all GUIs", boost::bind(notificationCallbackSuccess, this), boost::bind(notificationCallbackError, _1));
  2369. }
  2370. }
  2371. }
  2372. else
  2373. {
  2374. renderGuisActive = !renderGuisActive;
  2375. }
  2376. break;
  2377. case SDLK_F8:
  2378. if ( event->isKeyPressedWithCtrlEvent(event->getKeyCode()) )
  2379. {
  2380. if( getWorkspace()->getWorld() && getWorkspace()->getWorld()->getKernel() )
  2381. {
  2382. getWorkspace()->getWorld()->getKernel()->dumpLog( !event->isKeyPressedWithShiftEvent(event->getKeyCode()) );
  2383. }
  2384. }
  2385. break;
  2386.  
  2387. case SDLK_F10:
  2388. if(event->isKeyPressedWithShiftEvent(event->getKeyCode()))
  2389. graphicsQualityShortcutSignal(false);
  2390. else
  2391. graphicsQualityShortcutSignal(true);
  2392. break;
  2393.  
  2394. case SDLK_F11:
  2395. verb = getWhitelistVerb("Toggle", "Full", "Screen");
  2396. break;
  2397.  
  2398. case SDLK_F12:
  2399. verb = getWhitelistVerb("Record", "Toggle", "");
  2400. break;
  2401.  
  2402. case SDLK_SYSREQ: // This is technically print ...... directx doesn't have a print key code :(
  2403. case SDLK_PRINT:
  2404. verb = getWhitelistVerb("", "Screen", "shot");
  2405. break;
  2406. case SDLK_LALT:
  2407. case SDLK_RALT:
  2408. RBX::GameBasicSettings::singleton().setFreeLook(true); // if we are in camlock, holding alt (option on mac) allows us to pan camera freely
  2409. break;
  2410.  
  2411. case SDLK_ESCAPE:
  2412. if (RBX::GuiService* guiService = RBX::ServiceProvider::find<RBX::GuiService>(this))
  2413. guiService->escapeKeyPressed();
  2414. break;
  2415. default:
  2416. break;
  2417. }
  2418. }
  2419. }
  2420. break;
  2421. }
  2422. default: break;
  2423. }
  2424. if (verb && verb->isEnabled()) {
  2425. Verb::doItWithChecks(verb, this);
  2426. if (!preventSinking)
  2427. return GuiResponse::sunk();
  2428. }
  2429.  
  2430. return GuiResponse::notSunk();
  2431. }
  2432.  
  2433. GuiResponse DataModel::processPlayerGui(const shared_ptr<InputObject>& event)
  2434. {
  2435. RBX::Network::Players* players = ServiceProvider::create<RBX::Network::Players>(this);
  2436.  
  2437. if (RBX::Network::Player* player = players->getLocalPlayer())
  2438. if (PlayerGui* playerGui = player->findFirstChildOfType<PlayerGui>())
  2439. return playerGui->process(event);
  2440.  
  2441. return GuiResponse::notSunk();
  2442. }
  2443.  
  2444. GuiResponse DataModel::processCameraCommands(const shared_ptr<InputObject>& event)
  2445. {
  2446. Verb* verb = NULL;
  2447.  
  2448.  
  2449. if (FFlag::UserAllCamerasInLua)
  2450. {
  2451. if (const Camera* camera = workspace->getConstCamera())
  2452. {
  2453. if (camera->hasClientPlayer())
  2454. {
  2455. return GuiResponse::notSunk();
  2456. }
  2457. }
  2458. }
  2459.  
  2460. if (!FFlag::UserBetterInertialScrolling)
  2461. {
  2462. switch(event->getUserInputType())
  2463. {
  2464. case InputObject::TYPE_MOUSEWHEEL:
  2465. {
  2466. if (const Camera* camera = workspace->getConstCamera())
  2467. {
  2468. if (camera->getCameraType() == Camera::CUSTOM_CAMERA)
  2469. {
  2470. if (RBX::Network::Players::findLocalPlayer(this))
  2471. {
  2472. break;
  2473. }
  2474. }
  2475. }
  2476.  
  2477. if (userInputService && !userInputService->isCtrlDown() && !userInputService->isShiftDown() && !userInputService->isAltDown())
  2478. {
  2479. if (event->isMouseWheelForward())
  2480. verb = workspace->getWhitelistVerb("Camera", "Zoom", "In");
  2481. else if(event->isMouseWheelBackward())
  2482. verb = workspace->getWhitelistVerb("Camera", "Zoom", "Out");
  2483. }
  2484.  
  2485. break;
  2486. }
  2487. default:
  2488. break;
  2489. }
  2490. }
  2491.  
  2492. if (FFlag::UserBetterInertialScrolling)
  2493. {
  2494. if (event->isMouseWheelForward() || event->isMouseWheelBackward())
  2495. {
  2496. if (Camera* camera = workspace->getCamera())
  2497. {
  2498. camera->zoom(event->getPosition().z);
  2499. }
  2500. }
  2501. }
  2502. else
  2503. {
  2504. if (verb && verb->isEnabled())
  2505. {
  2506. Verb::doItWithChecks(verb, this);
  2507. }
  2508. }
  2509.  
  2510. return GuiResponse::notSunk(); // don't sink this event when we fire it
  2511. }
  2512.  
  2513. void DataModel::setInitialScreenSize(RBX::Vector2 newScreenSize)
  2514. {
  2515. if (shared_ptr<RBX::ScreenGui> coreScreenGui = coreGuiService->getRobloxScreenGui())
  2516. {
  2517. coreScreenGui->setBufferedViewport(Rect2D(newScreenSize));
  2518. }
  2519. }
  2520.  
  2521. GuiResponse DataModel::processDevGamepadEvent(const shared_ptr<InputObject>& event)
  2522. {
  2523. if (GamepadService* gamepadService = ServiceProvider::find<RBX::GamepadService>(this))
  2524. {
  2525. return gamepadService->processDev(event);
  2526. }
  2527.  
  2528. return GuiResponse::notSunk();
  2529. }
  2530.  
  2531. GuiResponse DataModel::processCoreGamepadEvent(const shared_ptr<InputObject>& event)
  2532. {
  2533. if (GamepadService* gamepadService = ServiceProvider::find<RBX::GamepadService>(this))
  2534. {
  2535. return gamepadService->processCore(event);
  2536. }
  2537.  
  2538. return GuiResponse::notSunk();
  2539. }
  2540.  
  2541. GuiResponse DataModel::processProfilerEvent(const shared_ptr<InputObject>& event)
  2542. {
  2543. if (event->isMouseEvent())
  2544. {
  2545. unsigned int flags = Profiler::Flag_MouseMove;
  2546.  
  2547. int mouseX = event->getRawPosition().x;
  2548. int mouseY = event->getRawPosition().y;
  2549. int mouseWheel = 0;
  2550. int mouseButton = 0;
  2551.  
  2552. if (event->isMouseWheelEvent())
  2553. {
  2554. flags |= Profiler::Flag_MouseWheel;
  2555. mouseWheel = event->getRawPosition().z;
  2556. }
  2557.  
  2558. if (event->isMouseDownEvent())
  2559. {
  2560. flags |= Profiler::Flag_MouseDown;
  2561. mouseButton = event->getUserInputType() - InputObject::TYPE_MOUSEBUTTON1;
  2562. }
  2563.  
  2564. if (event->isMouseUpEvent())
  2565. {
  2566. flags |= Profiler::Flag_MouseUp;
  2567. mouseButton = event->getUserInputType() - InputObject::TYPE_MOUSEBUTTON1;
  2568. }
  2569.  
  2570. return Profiler::handleMouse(flags, mouseX, mouseY, mouseWheel, mouseButton) ? GuiResponse::sunk() : GuiResponse::notSunk();
  2571. }
  2572.  
  2573. if (event->isKeyDownEvent())
  2574. {
  2575. unsigned int ctrlOrCmd = KMOD_LCTRL | KMOD_RCTRL | KMOD_LMETA | KMOD_RMETA;
  2576.  
  2577. if (event->isKeyDownEvent(SDLK_F6) && (event->mod & ctrlOrCmd))
  2578. return Profiler::toggleVisible() ? GuiResponse::sunk() : GuiResponse::notSunk();
  2579.  
  2580. if (event->isKeyDownEvent(SDLK_p) && (event->mod & ctrlOrCmd))
  2581. return Profiler::togglePause() ? GuiResponse::sunk() : GuiResponse::notSunk();
  2582. }
  2583.  
  2584. return GuiResponse::notSunk();
  2585. }
  2586.  
  2587. GuiResponse DataModel::processGuiTarget(const shared_ptr<InputObject>& event)
  2588. {
  2589. GuiResponse response = GuiResponse::notSunk();
  2590.  
  2591. if (shared_ptr<Instance> guiTarget = guiTargetInstance.lock())
  2592. {
  2593. FASTLOG1(FLog::UserInputProfile, "Handing to GUI target: %p", guiTarget.get());
  2594. if (TextBox* textbox = fastDynamicCast<TextBox>(guiTarget.get()))
  2595. {
  2596. if (event->isKeyEvent() || event->isGamepadEvent())
  2597. {
  2598. response = textbox->process(event);
  2599. }
  2600. else if (!DFFlag::DontProcessMouseEventsForGuiTarget)
  2601. {
  2602. response = textbox->preProcess(event);
  2603. }
  2604. }
  2605. else if (GuiLayerCollector* guiCollector = fastDynamicCast<GuiLayerCollector>(guiTarget.get()))
  2606. {
  2607. response = guiCollector->process(event);
  2608. }
  2609. }
  2610.  
  2611. return response;
  2612. }
  2613.  
  2614. bool DataModel::processEvent(const shared_ptr<InputObject>& event)
  2615. {
  2616. RBXASSERT(event->getUserInputType() != InputObject::TYPE_NONE);
  2617. if (!isInitialized)
  2618. return false;
  2619.  
  2620. if (event->getUserInputType() == InputObject::TYPE_MOUSEMOVEMENT)
  2621. mouseStats.mouseMove.sample();
  2622.  
  2623. // Hack #2
  2624. switch(event->getUserInputType())
  2625. {
  2626. case InputObject::TYPE_MOUSEBUTTON2:
  2627. if (event->getUserInputState() == InputObject::INPUT_STATE_END) // button up
  2628. {
  2629. FASTLOG(FLog::UserInputProfile, "Cancelling Right mouse pan");
  2630. workspace->cancelRightMousePan();
  2631. }
  2632. break;
  2633. default:
  2634. break;
  2635. }
  2636.  
  2637. bool isInMenu = false;
  2638. if (RBX::GuiService* guiService = RBX::ServiceProvider::find<RBX::GuiService>(this))
  2639. {
  2640. isInMenu = guiService->getMenuOpen();
  2641. }
  2642.  
  2643. GuiResponse response = GuiResponse();
  2644. shared_ptr<Instance> guiTarget = guiTargetInstance.lock();
  2645.  
  2646. if (!guiTarget)
  2647. setSuppressNavKeys(false); // no target - make sure we no longer suppress asdf, which we do when a TextBox gets the focus/is target
  2648.  
  2649. FASTLOG(FLog::UserInputProfile, "Starting handing out events...");
  2650.  
  2651. if (event->getUserInputType() == InputObject::TYPE_MOUSEMOVEMENT)
  2652. {
  2653. mouseOverInteractable = weak_ptr<GuiObject>();
  2654. }
  2655.  
  2656. mouseOverGui = false;
  2657. if (!response.wasSunk())
  2658. {
  2659. FASTLOG(FLog::UserInputProfile, "Handing to Profiler");
  2660. response = processProfilerEvent(event);
  2661. }
  2662. if (DFFlag::ProcessAcceleratorsBeforeGUINavigation)
  2663. {
  2664.  
  2665. if (!isInMenu)
  2666. {
  2667. FASTLOG(FLog::UserInputProfile, "Handing to Accelerators");
  2668. response = processAccelerators(event);
  2669. }
  2670. }
  2671.  
  2672. if (!response.wasSunk())
  2673. {
  2674. response = processGuiTarget(event);
  2675. }
  2676.  
  2677. // todo: remove guiRoot when profiler has all the same stats
  2678. if (!response.wasSunk())
  2679. {
  2680. FASTLOG(FLog::UserInputProfile, "Handing to GUI root");
  2681. response = guiRoot->process(event);
  2682. }
  2683.  
  2684. if (!response.wasSunk() && (renderGuisActive || !DFFlag::AllowHideHudShortcut))
  2685. {
  2686. FASTLOG(FLog::UserInputProfile, "Handing to Core Gui");
  2687. response = coreGuiService->process(event);
  2688. mouseOverGui = response.getMouseWasOver();
  2689. }
  2690.  
  2691. if (!response.wasSunk() && event->isKeyDownEvent() && !suppressNavKeys)
  2692. {
  2693. if (GamepadService* gamepadService = RBX::ServiceProvider::create<GamepadService>(this))
  2694. {
  2695. FASTLOG(FLog::UserInputProfile, "Handing to gamepadService for keyboard event");
  2696. response = gamepadService->trySelectGuiObject(userInputService->getKeyboardGuiSelectionDirection(event));
  2697. }
  2698. }
  2699.  
  2700. if (!response.wasSunk())
  2701. {
  2702. FASTLOG(FLog::UserInputProfile, "Handing to processCoreGamepadEvent");
  2703. response = processCoreGamepadEvent(event);
  2704. }
  2705.  
  2706. if (!response.wasSunk())
  2707. {
  2708. FASTLOG(FLog::UserInputProfile, "Handing to ContextActionService Core Bindings");
  2709. response = contextActionService->processCoreBindings(event);
  2710. }
  2711.  
  2712. if (!DFFlag::ProcessAcceleratorsBeforeGUINavigation)
  2713. {
  2714. if (!response.wasSunk())
  2715. {
  2716. FASTLOG(FLog::UserInputProfile, "Handing to Accelerators");
  2717. response = processAccelerators(event);
  2718. }
  2719. }
  2720.  
  2721. if (!response.wasSunk() && (renderGuisActive || !DFFlag::AllowHideHudShortcut) && !isInMenu)
  2722. {
  2723. FASTLOG(FLog::UserInputProfile, "Handing to Player Gui");
  2724. response = processPlayerGui(event);
  2725. mouseOverGui = response.getMouseWasOver();
  2726.  
  2727. if ( response.getMouseWasOver())
  2728. {
  2729. if (GuiButton* responseButton = fastDynamicCast<GuiButton>(response.getTarget().get()))
  2730. {
  2731. mouseOverInteractable = weak_from(responseButton);
  2732. }
  2733. if (TextBox* responseBox = fastDynamicCast<TextBox>(response.getTarget().get()))
  2734. {
  2735. mouseOverInteractable = weak_from(responseBox);
  2736. }
  2737. }
  2738. }
  2739.  
  2740. if (!response.wasSunk() && !isInMenu)
  2741. {
  2742. FASTLOG(FLog::UserInputProfile, "Handing to processDevGamepadEvent");
  2743. response = processDevGamepadEvent(event);
  2744. }
  2745.  
  2746. if (!response.wasSunk())
  2747. {
  2748. FASTLOG(FLog::UserInputProfile, "Handing to ContextActionService Dev Bindings");
  2749. response = contextActionService->processDevBindings(event, isInMenu);
  2750. }
  2751.  
  2752. if (!response.wasSunk() && !isInMenu)
  2753. {
  2754. FASTLOG(FLog::UserInputProfile, "Handing to Camera Process");
  2755. response = processCameraCommands(event);
  2756. }
  2757.  
  2758. InputObject::UserInputType lastType = userInputService->getLastInputType();
  2759. if (GamepadService::getGamepadIntForEnum(lastType) == -1)
  2760. {
  2761. forceArrowCursor = (response.wasSunk() && guiTarget && (guiTarget.get() != workspace.get())) || mouseOverInteractable.lock();
  2762. }
  2763.  
  2764. const bool wasSunkBeforeHandingToTool = response.wasSunk();
  2765.  
  2766. if (!response.wasSunk() && !isInMenu && !event->isTouchEvent() && event->isPublicEvent())
  2767. {
  2768. FASTLOG(FLog::UserInputProfile, "Handing to Workspace");
  2769. response = workspace->process(event);
  2770. }
  2771.  
  2772. // Check for player idle (if we have a mouse event or a key was pressed, signal we aren't idle)
  2773. if (event->isMouseEvent() || event->isKeyDownEvent() || event->isGamepadEvent())
  2774. Network::Player::onLocalPlayerNotIdle(this);
  2775.  
  2776. if (DFFlag::DontProcessMouseEventsForGuiTarget)
  2777. {
  2778. // If a textbox is currently focused and the target is not that textbox then release focus from that textbox
  2779. if (TextBox* textbox = fastDynamicCast<TextBox>(guiTarget.get()))
  2780. {
  2781. if (textbox->getFocused() && (guiTarget != response.getTarget()))
  2782. {
  2783. textbox->releaseFocus(false, event);
  2784. }
  2785. }
  2786. }
  2787.  
  2788. guiTargetInstance = response.getTarget();
  2789.  
  2790. FASTLOG(FLog::UserInputProfile, "DM event processed");
  2791.  
  2792. return wasSunkBeforeHandingToTool;
  2793. }
  2794.  
  2795. void DataModel::processWorkspaceEvent(const shared_ptr<InputObject>& event)
  2796. {
  2797. GuiResponse response = GuiResponse();
  2798.  
  2799. FASTLOG(FLog::UserInputProfile, "Handing to Workspace via processWorkspaceEvent");
  2800. response = workspace->process(event);
  2801. }
  2802.  
  2803. bool DataModel::processInputObject(shared_ptr<InputObject> event)
  2804. {
  2805. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  2806. if (!isInitialized || !event)
  2807. return false;
  2808.  
  2809. bool processedEvent = false;
  2810.  
  2811. if (event->getUserInputType() != InputObject::TYPE_NONE)
  2812. processedEvent = processEvent(event);
  2813.  
  2814. InputObjectProcessed(event);
  2815.  
  2816. if (Network::Player* player = Network::Players::findLocalPlayer(this))
  2817. {
  2818. if (shared_ptr<Mouse> playerMouse = player->getMouse())
  2819. {
  2820. if(!processedEvent || event->getUserInputType() == InputObject::TYPE_MOUSEMOVEMENT)
  2821. {
  2822. playerMouse->update(event);
  2823. }
  2824. else if (event->isMouseEvent())
  2825. {
  2826. playerMouse->cacheInputObject(event);
  2827. }
  2828. }
  2829. }
  2830.  
  2831. RBXASSERT(isInitialized); // Show to David or Erik - threading issue
  2832.  
  2833. return processedEvent;
  2834. }
  2835.  
  2836. void DataModel::close()
  2837. {
  2838. Verb* verb = getWhitelistVerb("E", "xi", "t");
  2839. if (verb==NULL)
  2840. throw std::runtime_error("Couldn't find Exit Verb");
  2841. Verb::doItWithChecks(verb, NULL);
  2842. }
  2843.  
  2844. void DataModel::addCustomStat(std::string name, std::string value)
  2845. {
  2846. guiBuilder.addCustomStat(name, value);
  2847. }
  2848.  
  2849. void DataModel::removeCustomStat(std::string str)
  2850. {
  2851. guiBuilder.removeCustomStat(str);
  2852. }
  2853.  
  2854. void DataModel::writeStatsSettings()
  2855. {
  2856. guiBuilder.saveCustomStats();
  2857. }
  2858.  
  2859.  
  2860. void DataModel::clearContents(bool resettingSimulation)
  2861. {
  2862. // TODO: Should Service have a "clearContents()" function?
  2863.  
  2864. ScriptContext* sc = find<ScriptContext>();
  2865. if (sc)
  2866. {
  2867. if (DFFlag::CloseStatesBeforeChildRemoval)
  2868. {
  2869. FASTLOG(FLog::CloseDataModel, "Closing Script Context");
  2870. sc->closeStates(resettingSimulation);
  2871. ScriptContext::propScriptsDisabled.setValue(sc, false);
  2872. }
  2873. else
  2874. {
  2875. sc->setPreventNewConnections();
  2876. }
  2877. }
  2878.  
  2879. if (workspace)
  2880. {
  2881. // unlock terrain
  2882. if( workspace->getTerrain() ) workspace->getTerrain()->unlockParent();
  2883.  
  2884. workspace->setMouseCommand(shared_ptr<MouseCommand>());
  2885.  
  2886. if(workspace->getWorld() && workspace->getWorld()->getContactManager())
  2887. {
  2888. // spatial hash is slow to remove. do a special trick when we know we are removing everything.
  2889. workspace->getWorld()->getContactManager()->fastClear();
  2890. }
  2891.  
  2892. if(World* world = workspace->getWorld()){
  2893. FASTLOG3(FLog::CloseDataModel, "Clearing World -- %d Bodies, %d Points, %d Constraints", world->getNumBodies(), world->getNumPoints(), world->getNumConstraints());
  2894. FASTLOG3(FLog::CloseDataModel, "Clearing World -- %d HashNodes, %d MaxBucketSize, %d NumLinkCalls", world->getNumHashNodes(), world->getMaxBucketSize(), world->getNumLinkCalls());
  2895. FASTLOG3(FLog::CloseDataModel, "Clearing World -- %d Contacts, %d Joints, %d Primitives", world->getNumContacts(), world->getNumJoints(), world->getNumPrimitives());
  2896. }
  2897.  
  2898. FASTLOG3(FLog::CloseDataModel, "Clearing Workspace -- %d Instances, %d Parts, %d Scripts", workspace->countDescendantsOfType<Instance>(), workspace->countDescendantsOfType<PartInstance>(), workspace->countDescendantsOfType<BaseScript>());
  2899. // TODO: Should we simply remove children of ALL service???
  2900. workspace->removeAllChildren();
  2901. workspace->setCurrentCamera(NULL);
  2902. }
  2903.  
  2904. if (starterPackService){
  2905. FASTLOG1(FLog::CloseDataModel, "Clearing StarterPack -- %d Instances", starterPackService->countDescendantsOfType<Instance>());
  2906. starterPackService->removeAllChildren();
  2907. }
  2908.  
  2909. if (starterPlayerService){
  2910. FASTLOG1(FLog::CloseDataModel, "Clearing StarterPlayer -- %d Instances", starterPlayerService->countDescendantsOfType<Instance>());
  2911. starterPlayerService->removeAllChildren();
  2912. }
  2913.  
  2914. if (starterGuiService){
  2915. FASTLOG1(FLog::CloseDataModel, "Clearing StarterGui -- %d Instances", starterGuiService->countDescendantsOfType<Instance>());
  2916. starterGuiService->removeAllChildren();
  2917. }
  2918.  
  2919. // only remove core gui if we are closing doc
  2920. if (coreGuiService && !resettingSimulation){
  2921. FASTLOG1(FLog::CloseDataModel, "Clearing CoreGui -- %d Instances", coreGuiService->countDescendantsOfType<Instance>());
  2922. coreGuiService->removeAllChildren();
  2923. }
  2924.  
  2925. if (Teams* teams = ServiceProvider::find<Teams>(this)) {
  2926. FASTLOG1(FLog::CloseDataModel, "Clearing Teams -- %d Instances", teams->countDescendantsOfType<Instance>());
  2927. teams->removeAllChildren();
  2928. }
  2929.  
  2930. if (SpawnerService* ss = ServiceProvider::find<SpawnerService>(this))
  2931. ss->ClearContents();
  2932.  
  2933. if (ChangeHistoryService* ch = ServiceProvider::find<ChangeHistoryService>(this)){
  2934. FASTLOG1(FLog::CloseDataModel, "Clearing ChangeHistoryService -- %d waypoints ", ch->getWaypointCount());
  2935. ch->clearWaypoints();
  2936. }
  2937.  
  2938. if (Lighting* lighting = ServiceProvider::find<Lighting>(this)) {
  2939. FASTLOG1(FLog::CloseDataModel, "Clearing Lighting -- %d Instances", lighting->countDescendantsOfType<Instance>());
  2940. lighting->removeAllChildren();
  2941. }
  2942.  
  2943. if (Instance* service = ServiceProvider::find<JointsService>(this)) {
  2944. FASTLOG1(FLog::CloseDataModel, "Clearing JointsService -- %d Instances", service->countDescendantsOfType<Instance>());
  2945. service->removeAllChildren();
  2946. }
  2947.  
  2948. if (Instance* service = ServiceProvider::find<TestService>(this)) {
  2949. FASTLOG1(FLog::CloseDataModel, "Clearing TestService -- %d Instances", service->countDescendantsOfType<Instance>());
  2950. service->removeAllChildren();
  2951. }
  2952.  
  2953. if (Instance* service = ServiceProvider::find<ServerScriptService>(this)) {
  2954. FASTLOG1(FLog::CloseDataModel, "Clearing ServerScriptService -- %d Instances", service->countDescendantsOfType<Instance>());
  2955. service->removeAllChildren();
  2956. }
  2957.  
  2958. if (Instance* service = ServiceProvider::find<ReplicatedStorage>(this)) {
  2959. FASTLOG1(FLog::CloseDataModel, "Clearing ReplicatedStorage -- %d Instances", service->countDescendantsOfType<Instance>());
  2960. service->removeAllChildren();
  2961. }
  2962.  
  2963. if (Instance* service = ServiceProvider::find<RobloxReplicatedStorage>(this)) {
  2964. FASTLOG1(FLog::CloseDataModel, "Clearing RobloxReplicatedStorage -- %d Instances", service->countDescendantsOfType<Instance>());
  2965. service->removeAllChildren();
  2966. }
  2967.  
  2968. if (Instance* service = ServiceProvider::find<ReplicatedFirst>(this)) {
  2969. FASTLOG1(FLog::CloseDataModel, "Clearing ReplicatedFirst -- %d Instances", service->countDescendantsOfType<Instance>());
  2970. service->removeAllChildren();
  2971. }
  2972.  
  2973. if (Instance* service = ServiceProvider::find<ServerStorage>(this)) {
  2974. FASTLOG1(FLog::CloseDataModel, "Clearing ServerStorage -- %d Instances", service->countDescendantsOfType<Instance>());
  2975. service->removeAllChildren();
  2976. }
  2977.  
  2978. if (sc && !DFFlag::CloseStatesBeforeChildRemoval)
  2979. {
  2980. FASTLOG(FLog::CloseDataModel, "Closing Script Context");
  2981. sc->closeStates(resettingSimulation);
  2982. ScriptContext::propScriptsDisabled.setValue(sc, false);
  2983. }
  2984. }
  2985.  
  2986.  
  2987. static std::string report(double time)
  2988. {
  2989. if (time > 0.0)
  2990. {
  2991. char buffer[256];
  2992. std::string t = Log::formatTime(time);
  2993. int cpu = static_cast<int>(100 * time * 30.0);
  2994. sprintf(buffer, "%i %% %s (%.3gfps)", cpu, t.c_str(), 1.0/time);
  2995. return buffer;
  2996. }
  2997. else
  2998. return Log::formatTime(time);
  2999. }
  3000.  
  3001. void DataModel::setNetworkMetric(IMetric* metric)
  3002. {
  3003. networkMetric = metric;
  3004. }
  3005.  
  3006. double DataModel::getMetricValue(const std::string& metric) const
  3007. {
  3008. if (metric == "Render FPS")
  3009. {
  3010. return tempMetric->getMetricValue("Render FPS");
  3011. }
  3012. if (metric == "Render CPU")
  3013. {
  3014. return 100.0 * tempMetric->getMetricValue("Render Duty");
  3015. }
  3016. if (metric == "Render Time")
  3017. {
  3018. return 1000.0 * tempMetric->getMetricValue("Render Job Time");
  3019. }
  3020. if (metric == "Physics FPS")
  3021. {
  3022. return runService->smoothFps();
  3023. }
  3024. if (metric == "Physics CPU")
  3025. {
  3026. return 100.0 * runService->physicsCpuFraction();
  3027. }
  3028. if (metric == "Physics Time")
  3029. {
  3030. return 1000.0 * runService->physicsAverageStep();
  3031. }
  3032. if (metric == "Network Receive CPU")
  3033. {
  3034. return networkMetric ? networkMetric->getMetricValue("Network Receive CPU") : 0.0;
  3035. }
  3036. if (metric == "Network Receive Time")
  3037. {
  3038. return networkMetric ? networkMetric->getMetricValue("Network Receive Time") : 0.0;
  3039. }
  3040. if (metric == "Frame Time")
  3041. {
  3042. return tempMetric->getMetricValue("Delta Between Renders");
  3043. }
  3044. if (metric == "Effective FPS")
  3045. {
  3046. return 1000.0 / tempMetric->getMetricValue("Delta Between Renders");
  3047. }
  3048.  
  3049. Stats::StatsService* stats = find<Stats::StatsService>();
  3050. if (stats)
  3051. {
  3052. shared_ptr<Stats::Item> network = shared_from_polymorphic_downcast<Stats::Item>(stats->findFirstChildByName("Network"));
  3053. if (network && network->numChildren() > 1)
  3054. {
  3055. if (Instance* item = network->getChild(1))
  3056. {
  3057. if (metric == "Received Physics Packets")
  3058. {
  3059. return item->findFirstChildByName("Received Physics Packets")->fastDynamicCast<Stats::Item>()->getValue();
  3060. }
  3061. if (metric == "Data Ping")
  3062. {
  3063. return item->findFirstChildByName("Data Ping")->fastDynamicCast<Stats::Item>()->getValue();
  3064. }
  3065. }
  3066. else
  3067. {
  3068. return 0.0;
  3069. }
  3070. }
  3071. else
  3072. {
  3073. return 0.0;
  3074. }
  3075. }
  3076. else
  3077. {
  3078. return 0.0;
  3079. }
  3080.  
  3081. throw RBX::runtime_error("%s is not a valid metric.", metric.c_str());
  3082. }
  3083.  
  3084. std::string DataModel::getMetric(const std::string& valueName) const
  3085. {
  3086. World* world = workspace->getWorld();
  3087.  
  3088. if (valueName.compare(0, 11, "RenderStats") == 0) return tempMetric->getMetric(valueName);
  3089. if (valueName.compare(0, 3, "FRM") == 0) return RBX::format("%.3g", tempMetric->getMetricValue(valueName));
  3090.  
  3091. Stats::StatsService* stats = find<Stats::StatsService>();
  3092. if (stats)
  3093. {
  3094. shared_ptr<Stats::Item> network = shared_from_polymorphic_downcast<Stats::Item>(stats->findFirstChildByName("Network"));
  3095. if (network && network->numChildren() > 1)
  3096. {
  3097. if (Instance* item = network->getChild(1))
  3098. {
  3099. // raknet
  3100. if (Stats::Item* raknetItem = Instance::fastDynamicCast<Stats::Item>(item->findFirstChildByName("Stats")->findFirstChildByName(valueName)))
  3101. return RBX::format("%.0f", raknetItem->getValue());
  3102.  
  3103. // replicator
  3104. if (valueName == "RakNetPing")
  3105. {
  3106. return item->findFirstChildByName("Ping")->fastDynamicCast<Stats::Item>()->getStringValue();
  3107. }
  3108. else if (valueName == "GeneralStats")
  3109. {
  3110. return RBX::format("%.0f, %.2fms"
  3111. , item->findFirstChildByName("Send kBps")->findFirstChildByName("MtuSize")->fastDynamicCast<Stats::Item>()->getValue()
  3112. , item->findFirstChildByName("Data Ping")->fastDynamicCast<Stats::Item>()->getValue()
  3113. );
  3114. }
  3115. else if (valueName == "OutgoingStats")
  3116. {
  3117. return RBX::format("%.2f"
  3118. , item->findFirstChildByName("Send kBps")->fastDynamicCast<Stats::Item>()->getValue()
  3119. );
  3120. }
  3121. else if (valueName == "IncomingStats")
  3122. {
  3123. return RBX::format("%.2f, %.2f, %.0f"
  3124. , item->findFirstChildByName("Receive kBps")->fastDynamicCast<Stats::Item>()->getValue()
  3125. , item->findFirstChildByName("Received Packets")->fastDynamicCast<Stats::Item>()->getValue()
  3126. , item->findFirstChildByName("Packet Queue")->fastDynamicCast<Stats::Item>()->getValue()
  3127. );
  3128. }
  3129. else if (valueName == "InData")
  3130. {
  3131. Stats::Item* receivedDataPackets = item->findFirstChildByName("Received Data Packets")->fastDynamicCast<Stats::Item>();
  3132. float numPacket = receivedDataPackets->getValue();
  3133. float avgSize = receivedDataPackets->findFirstChildByName("Size")->fastDynamicCast<Stats::Item>()->getValue();
  3134. float kBps = numPacket * avgSize / 1000.f;
  3135. return RBX::format("%.2f, %.2f, %.2fB", kBps, numPacket, avgSize);
  3136. }
  3137. else if (valueName == "InPhysics")
  3138. {
  3139. Stats::Item* inPhysics = item->findFirstChildByName("Received Physics Packets")->fastDynamicCast<Stats::Item>();
  3140. float numPacket = inPhysics->getValue();
  3141. float avgSize = inPhysics->findFirstChildByName("Size")->fastDynamicCast<Stats::Item>()->getValue();
  3142. float kBps = numPacket * avgSize / 1000.f;
  3143. return RBX::format("%.2f, %.2f, %.2fB, %.2f", kBps, numPacket, avgSize
  3144. , inPhysics->findFirstChildByName("Average Lag")->fastDynamicCast<Stats::Item>()->getValue());
  3145. }
  3146. else if (valueName == "InTouches")
  3147. {
  3148. Stats::Item* inTouches = item->findFirstChildByName("Received Touch Packets")->fastDynamicCast<Stats::Item>();
  3149. float numPacket = inTouches->getValue();
  3150. float avgSize = inTouches->findFirstChildByName("Size")->fastDynamicCast<Stats::Item>()->getValue();
  3151. float kBps = numPacket * avgSize / 1000.f;
  3152. return RBX::format("%.2f, %.2f, %.2fB", kBps, numPacket, avgSize);
  3153. }
  3154. else if (valueName == "InClusters")
  3155. {
  3156. Stats::Item* inClusters = item->findFirstChildByName("Received Cluster Packets")->fastDynamicCast<Stats::Item>();
  3157. float numPacket = inClusters->getValue();
  3158. float avgSize = inClusters->findFirstChildByName("Size")->fastDynamicCast<Stats::Item>()->getValue();
  3159. float kBps = numPacket * avgSize / 1000.f;
  3160. return RBX::format("%.2f, %.2f, %.2fB", kBps, numPacket, avgSize);
  3161. }
  3162. else if (valueName == "OutPhysics")
  3163. {
  3164. Stats::Item* outPhysics = item->findFirstChildByName("Sent Physics Packets")->fastDynamicCast<Stats::Item>();
  3165. float numPacket = outPhysics->getValue();
  3166. float avgSize = outPhysics->findFirstChildByName("Size")->fastDynamicCast<Stats::Item>()->getValue();
  3167. float kBps = numPacket * avgSize / 1000.f;
  3168. return RBX::format("%.2f, %.2f, %.2fB, %.2f%%", kBps, numPacket, avgSize
  3169. , outPhysics->findFirstChildByName("Throttle")->fastDynamicCast<Stats::Item>()->getValue());
  3170. }
  3171. else if (valueName == "OutData")
  3172. {
  3173. Stats::Item* outProps = item->findFirstChildByName("Sent Data Packets")->fastDynamicCast<Stats::Item>();
  3174. float numPacket = outProps->getValue();
  3175. float avgSize = outProps->findFirstChildByName("Size")->fastDynamicCast<Stats::Item>()->getValue();
  3176. float kBps = numPacket * avgSize / 1000.f;
  3177. return RBX::format("%.2f, %.2f, %.2fB, %.2f%%", kBps, numPacket, avgSize, outProps->findFirstChildByName("Throttle")->fastDynamicCast<Stats::Item>()->getValue());
  3178. }
  3179. else if (valueName == "OutTouches")
  3180. {
  3181. Stats::Item* outTouches = item->findFirstChildByName("SentTouchPackets")->fastDynamicCast<Stats::Item>();
  3182. float numPacket = outTouches->getValue();
  3183. float avgSize = outTouches->findFirstChildByName("Size")->fastDynamicCast<Stats::Item>()->getValue();
  3184. float kBps = numPacket * avgSize / 1000.f;
  3185. return RBX::format("%.2f, %.2f, %.2fB, %.0f", kBps, numPacket, avgSize, outTouches->findFirstChildByName("WaitingTouches")->fastDynamicCast<Stats::Item>()->getValue());
  3186. }
  3187. else if (valueName == "OutClusters")
  3188. {
  3189. Stats::Item* outClusters = item->findFirstChildByName("Sent Cluster Packets")->fastDynamicCast<Stats::Item>();
  3190. float numPacket = outClusters->getValue();
  3191. float avgSize = outClusters->findFirstChildByName("Size")->fastDynamicCast<Stats::Item>()->getValue();
  3192. float kBps = numPacket * avgSize / 1000.f;
  3193. return RBX::format("%.2f, %.2f, %.2fB", kBps, numPacket, avgSize);
  3194. }
  3195. else if (valueName == "InPhysicsDetails")
  3196. {
  3197. std::string result = "";
  3198. if (Stats::Item* inPhysicsDetails = item->findFirstChildByName(valueName)->fastDynamicCast<Stats::Item>())
  3199. {
  3200. inPhysicsDetails->visitChildren(boost::bind(&GuiBuilder::buildNetworkStatsOutput, _1, &result));
  3201. }
  3202. return result;
  3203. }
  3204. else if (valueName == "OutPhysicsDetails")
  3205. {
  3206. std::string result = "";
  3207. if (Stats::Item* outPhysicsDetails = item->findFirstChildByName(valueName)->fastDynamicCast<Stats::Item>())
  3208. {
  3209. outPhysicsDetails->visitChildren(boost::bind(&GuiBuilder::buildNetworkStatsOutput, _1, &result));
  3210. }
  3211. return result;
  3212. }
  3213. else if (valueName == "InDataDetails")
  3214. {
  3215. std::string result = "";
  3216. if (Stats::Item* inDataDetails = item->findFirstChildByName("Received Data Types")->fastDynamicCast<Stats::Item>())
  3217. {
  3218. inDataDetails->visitChildren(boost::bind(&GuiBuilder::buildNetworkStatsOutput, _1, &result));
  3219. }
  3220. return result;
  3221. }
  3222. else if (valueName == "OutDataDetails")
  3223. {
  3224. std::string result = "";
  3225. if (Stats::Item* inDataDetails = item->findFirstChildByName("Send Data Types")->fastDynamicCast<Stats::Item>())
  3226. {
  3227. inDataDetails->visitChildren(boost::bind(&GuiBuilder::buildNetworkStatsOutput, _1, &result));
  3228. }
  3229. return result;
  3230. }
  3231. else if (valueName == "FreeMemory")
  3232. {
  3233. float result = RBX::NetworkSettings::singleton().getFreeMemoryMBytes();
  3234. return RBX::format("%.2fMB", result);
  3235. }
  3236. else if (valueName == "MemoryLevel")
  3237. {
  3238. int result = RBX::MemoryStats::slowCheckMemoryLevel(((RBX::MemoryStats::memsize_t)RBX::NetworkSettings::singleton().getExtraMemoryUsedInMB())*1024*1024);
  3239. return RBX::format("%d", result);
  3240. }
  3241. else if (valueName == "ReceivedStreamData")
  3242. {
  3243. std::string result = "";
  3244. if (Stats::Item* streamStats = item->findFirstChildByName("Received Stream Data")->fastDynamicCast<Stats::Item>())
  3245. {
  3246. streamStats->visitChildren(boost::bind(&GuiBuilder::buildSimpleStatsOutput, _1, &result));
  3247. }
  3248. return result;
  3249. }
  3250. }
  3251. }
  3252.  
  3253. shared_ptr<Stats::Item> httpQueue = shared_from_polymorphic_downcast<Stats::Item>(stats->findFirstChildByName("HttpQueue_ContentProvider"));
  3254. if (httpQueue)
  3255. {
  3256. if (valueName == "HttpTimeInQueue")
  3257. {
  3258. return RBX::format("%.2f msec", httpQueue->findFirstChildByName("Average time in queue")->fastDynamicCast<Stats::Item>()->getValue());
  3259. }
  3260. else if (valueName == "HttpProcessTime")
  3261. {
  3262. return RBX::format("%.2f msec", httpQueue->findFirstChildByName("Average process time")->fastDynamicCast<Stats::Item>()->getValue());
  3263. }
  3264. else if (valueName == "HttpSlowReq")
  3265. {
  3266. return RBX::format("%.0f", httpQueue->findFirstChildByName("Num slow requests")->fastDynamicCast<Stats::Item>()->getValue());
  3267. }
  3268. }
  3269.  
  3270. }
  3271.  
  3272. if (valueName == "physicsMode") {
  3273. return Network::Players::getDistributedPhysicsEnabled() ? "Experimental Physics" : "Standard Physics";
  3274. }
  3275.  
  3276. else if (valueName == "Physics") // This is the frequency at which physics job is actually executed
  3277. {
  3278. boost::format fmt("%.1f/s %.1f msec %d%%");
  3279. fmt % runService->smoothFps() % (1000.0 * runService->physicsAverageStep()) % (int)(100.0 * runService->physicsCpuFraction());
  3280. return fmt.str();
  3281. }
  3282.  
  3283. else if (valueName == "PhysicsReal") // This is the effective physics frame-rate considering steps skipped by environmental throttling
  3284. {
  3285. boost::format fmt("%.1f/s throttle@%d%%(%d)");
  3286. fmt % (runService->smoothFps() * world->getEnvironmentSpeed()) % (int)(100.0 * world->getEnvironmentSpeed()) % world->getFRMThrottle();
  3287. return fmt.str();
  3288. }
  3289.  
  3290. else if (valueName == "Heartbeat")
  3291. {
  3292. boost::format fmt("%.1f/s %.1f msec %d%%");
  3293. fmt % runService->heartbeatFps() % (1000.0 * runService->heartbeatAverageStep()) % (int)(100.0 * runService->heartbeatCpuFraction());
  3294. return fmt.str();
  3295. }
  3296.  
  3297. else if (valueName == "Network Send" || valueName == "Network Receive")
  3298. {
  3299. return networkMetric ? networkMetric->getMetric(valueName) : "";
  3300. }
  3301.  
  3302. else if (valueName == "Render")
  3303. {
  3304. boost::format fmt("%.1f/s %.1f msec %d%%");
  3305. fmt % tempMetric->getMetricValue("Render FPS") % (1000.0 * tempMetric->getMetricValue("Render Job Time")) % (int)(100.0 * tempMetric->getMetricValue("Render Duty"));
  3306. return fmt.str();
  3307. }
  3308.  
  3309. else if (valueName == "MouseMove")
  3310. {
  3311. boost::format fmt("%.1f/s -> %.1f/s");
  3312. fmt % mouseStats.osMouseMove.rate() % mouseStats.mouseMove.rate();
  3313. return fmt.str();
  3314. }
  3315.  
  3316. else if (valueName == "Effective FPS") return RBX::format("%.1f/s", 1000.0 / tempMetric->getMetricValue("Delta Between Renders"));
  3317. else if (valueName == "Render Nominal FPS") return RBX::format("%.1f/s", tempMetric->getMetricValue(valueName));
  3318. else if (valueName == "Delta Between Renders") return RBX::format("%.3g", tempMetric->getMetricValue(valueName));
  3319. else if (valueName == "Total Render") return RBX::format("%.3g", tempMetric->getMetricValue(valueName));
  3320. else if (valueName == "Render Prepare") return RBX::format("%.3g", tempMetric->getMetricValue(valueName));
  3321. else if (valueName == "Present Time") return RBX::format("%.3g", tempMetric->getMetricValue(valueName));
  3322. else if (valueName == "GPU Delay") return RBX::format("%.3g", tempMetric->getMetricValue(valueName));
  3323.  
  3324. else if (valueName == "RequestQueueSize") return StringConverter<int>::convertToString(ServiceProvider::create<ContentProvider>()->getRequestQueueSize());
  3325.  
  3326. else if (valueName == "drawId") return StringConverter<int>::convertToString(drawId);
  3327. else if (valueName == "worldId") return StringConverter<int>::convertToString(world->getWorldStepId());
  3328. else if (valueName == "Graphics Mode") return tempMetric->getMetric(valueName);
  3329. else if (valueName == "Particles") return StringConverter<int>::convertToString(static_cast<int>(tempMetric->getMetricValue(valueName)));
  3330. else if (valueName == "Shadow Casters") return StringConverter<int>::convertToString(static_cast<int>(tempMetric->getMetricValue(valueName)));
  3331. else if (valueName == "Anti-Aliasing") return tempMetric->getMetric(valueName);
  3332. else if (valueName == "Bevels") return tempMetric->getMetric(valueName);
  3333. else if (valueName == "Video Memory") return Log::formatMem(static_cast<int>(tempMetric->getMetricValue(valueName)));
  3334.  
  3335. else if (valueName == "Player Radius")
  3336. {
  3337. RBX::Network::Players* players = ServiceProvider::create<RBX::Network::Players>(this);
  3338. int numPlayers = players->getNumPlayers();
  3339. if (numPlayers == 0)
  3340. return std::string("N/A");
  3341. float sumRadius = 0.0f;
  3342. float sumMaxRaius = 0.0f;
  3343. shared_ptr<const Instances> myPlayers = players->getPlayers();
  3344. Instances::const_iterator iter = myPlayers->begin();
  3345. Instances::const_iterator end = myPlayers->end();
  3346. for (; iter!=end; ++iter)
  3347. {
  3348. RBX::Network::Player* player = boost::polymorphic_downcast<RBX::Network::Player*>(iter->get());
  3349. sumRadius += player->getSimulationRadius();
  3350. sumMaxRaius += player->getMaxSimulationRadius();
  3351. }
  3352. boost::format fmt("%d / %d");
  3353. fmt % static_cast<int>(sumRadius / numPlayers) % static_cast<int>(sumMaxRaius / numPlayers);
  3354. return fmt.str();
  3355. }
  3356. else if (valueName == "numPrimitives") return StringConverter<int>::convertToString(numPartInstances);
  3357. else if (valueName == "numMovingPrimitives") return StringConverter<int>::convertToString(workspace->getNumberMoving());
  3358. else if (valueName == "numJoints")
  3359. {
  3360. int numJoints = world->getNumJoints();
  3361. if (JointsService* jointsService = ServiceProvider::find<JointsService>(this))
  3362. {
  3363. numJoints += jointsService->numChildren();
  3364. }
  3365. return StringConverter<int>::convertToString(numJoints);
  3366. }
  3367. else if (valueName == "numInstances")
  3368. {
  3369. return StringConverter<int>::convertToString(Diagnostics::Countable<Instance>::getCount());
  3370. }
  3371. else if (valueName == "numContactsAll")
  3372. {
  3373. boost::format fmt("%d %d %d %d");
  3374. fmt % world->getNumContacts() %
  3375. world->getMetric(IWorldStage::NUM_CONTACTSTAGE_CONTACTS) %
  3376. world->getMetric(IWorldStage::NUM_STEPPING_CONTACTS) %
  3377. world->getMetric(IWorldStage::NUM_TOUCHING_CONTACTS);
  3378. return fmt.str();
  3379. }
  3380. else if (valueName == "numContacts") return StringConverter<int>::convertToString(world->getNumContacts());
  3381. else if (valueName == "numContactsInCollisionStage") return StringConverter<int>::convertToString(world->getMetric(IWorldStage::NUM_CONTACTSTAGE_CONTACTS));
  3382. else if (valueName == "numSteppingContacts") return StringConverter<int>::convertToString(world->getMetric(IWorldStage::NUM_STEPPING_CONTACTS));
  3383. else if (valueName == "numTouchingContacts") return StringConverter<int>::convertToString(world->getMetric(IWorldStage::NUM_TOUCHING_CONTACTS));
  3384. else if (valueName == "maxTreeDepth") return StringConverter<int>::convertToString(world->getMetric(IWorldStage::MAX_TREE_DEPTH));
  3385. else if (valueName == "contactPairHitRatio") return StringConverter<float>::convertToString(BlockBlockContact::pairHitRatio());
  3386. else if (valueName == "contactFeatureHitRatio") return StringConverter<float>::convertToString(BlockBlockContact::featureHitRatio());
  3387.  
  3388. else if (valueName == "numLinkCalls") return StringConverter<int>::convertToString(world->getNumLinkCalls());
  3389. else if (valueName == "numHashNodes") return StringConverter<int>::convertToString(world->getNumHashNodes());
  3390. else if (valueName == "maxBucketSize") return StringConverter<int>::convertToString(world->getMaxBucketSize());
  3391. else if (valueName == "environmentSpeed") return StringConverter<float>::convertToString(world->getEnvironmentSpeed());
  3392.  
  3393.  
  3394. else if (valueName == "Break Time" ||
  3395. valueName == "Assembler Time" ||
  3396. valueName == "Filter Time" ||
  3397. valueName == "UI Step" ||
  3398. valueName == "Broadphase" ||
  3399. valueName == "Collision" ||
  3400. valueName == "Wake" ||
  3401. valueName == "Sleep" ||
  3402. valueName == "Joint Update" ||
  3403. valueName == "Joint Sleep" ||
  3404. valueName == "Kernel Bodies" ||
  3405. valueName == "Kernel Connectors" )
  3406. {
  3407. if (stats)
  3408. {
  3409. shared_ptr<Stats::Item> workspace = shared_from_polymorphic_downcast<Stats::Item>(stats->findFirstChildByName("Workspace"));
  3410. if( workspace && workspace->numChildren() > 0)
  3411. {
  3412. shared_ptr<Stats::Item> dataModelStep = shared_from_polymorphic_downcast<Stats::Item>(workspace->findFirstChildByName("DataModel Step"));
  3413. if( dataModelStep && dataModelStep->numChildren() > 0)
  3414. {
  3415. shared_ptr<Stats::Item> workspaceStep = shared_from_polymorphic_downcast<Stats::Item>(dataModelStep->findFirstChildByName("Workspace Step"));
  3416. if( workspaceStep && workspaceStep->numChildren() > 0)
  3417. {
  3418. shared_ptr<Stats::Item> worldStep = shared_from_polymorphic_downcast<Stats::Item>(workspaceStep->findFirstChildByName("World Step"));
  3419. if (worldStep && worldStep->numChildren() > 0)
  3420. {
  3421. RBX::Instance* profilerItem = worldStep->findFirstChildByName(valueName);
  3422. RBX::Stats::Item* profilerStatItem = Instance::fastDynamicCast<RBX::Stats::Item>(profilerItem);
  3423. if(profilerStatItem)
  3424. return profilerStatItem->getStringValue();
  3425. }
  3426. }
  3427. }
  3428. }
  3429. }
  3430. }
  3431.  
  3432.  
  3433. // these are here for fun and to throw off the competition!
  3434. else if (valueName == "solverIterations") return StringConverter<int>::convertToString(world->getKernel()->fakeDeceptiveSolverIterations());
  3435. else if (valueName == "matrixSize") return StringConverter<int>::convertToString(world->getKernel()->fakeDeceptiveMatrixSize());
  3436.  
  3437.  
  3438. else if (valueName == "pGSSolverActive")
  3439. {
  3440. return StringConverter<bool>::convertToString(world->getUsingPGSSolver());
  3441. }
  3442.  
  3443. else if (valueName == "numBodiesAll")
  3444. {
  3445. boost::format fmt("%d %d %d %d %d %d");
  3446. fmt % world->getKernel()->numBodiesMax() %
  3447. world->getKernel()->numFreeFallBodies() %
  3448. world->getKernel()->numContactBodies() %
  3449. world->getKernel()->numJointBodies() %
  3450. world->getKernel()->numRealTimeBodies() %
  3451. world->getKernel()->numLeafBodies();
  3452. return fmt.str();
  3453. }
  3454.  
  3455. else if (valueName == "numConnectorsAll")
  3456. {
  3457. boost::format fmt("%d %d %d %d %d %d");
  3458. fmt % world->getKernel()->numConnectors() %
  3459. world->getKernel()->numContactConnectors() %
  3460. world->getKernel()->numJointConnectors() %
  3461. world->getKernel()->numRealTimeConnectors() %
  3462. world->getKernel()->numSecondPassConnectors() %
  3463. world->getKernel()->numHumanoidConnectors();
  3464. return fmt.str();
  3465. }
  3466.  
  3467. else if (valueName == "numBodies") return StringConverter<int>::convertToString(world->getKernel()->numBodies());
  3468. else if (valueName == "maxBodies") return StringConverter<int>::convertToString(world->getKernel()->numBodiesMax());
  3469. else if (valueName == "numLeafBodies") return StringConverter<int>::convertToString(world->getKernel()->numLeafBodies());
  3470. else if (valueName == "numConnectorsAndPoints")
  3471. {
  3472. boost::format fmt("%d %d");
  3473. fmt % world->getKernel()->numConnectors() % world->getKernel()->numPoints();
  3474. return fmt.str();
  3475. }
  3476.  
  3477. else if (valueName == "numIterations")
  3478. {
  3479. boost::format fmt("%d %d (%f %f)");
  3480. fmt % world->getKernel()->numMaxIterations() % world->getKernel()->numIterations()
  3481. % world->getKernel()->getMaxSolverError() % world->getKernel()->getSolverError();
  3482. return fmt.str();
  3483. }
  3484.  
  3485. else if (valueName == "numConstraints") return StringConverter<int>::convertToString(world->getKernel()->numConnectors());
  3486. else if (valueName == "numPoints") return StringConverter<int>::convertToString(world->getKernel()->numPoints());
  3487. else if (valueName == "percentConnectorsActive") return StringConverter<float>::convertToString(ContactConnector::percentActive());
  3488.  
  3489. else if (valueName == "energyBody") return StringConverter<float>::convertToString(world->getKernel()->bodyKineticEnergy());
  3490. else if (valueName == "energyConnector")return StringConverter<float>::convertToString(world->getKernel()->connectorSpringEnergy());
  3491. else if (valueName == "energyTotal") return StringConverter<float>::convertToString(world->getKernel()->totalKineticEnergy());
  3492.  
  3493. //RBXASSERT(0);
  3494. return "?";
  3495. }
  3496.  
  3497. DataModel* DataModel::get(Instance* context)
  3498. {
  3499. return context ? Instance::fastDynamicCast<DataModel>(context->getRootAncestor()) : NULL;
  3500. }
  3501. const DataModel* DataModel::get(const Instance* context)
  3502. {
  3503. return context ? Instance::fastDynamicCast<DataModel>(context->getRootAncestor()) : NULL;
  3504. }
  3505.  
  3506. static void appendJobInfo(DataModel* dataModel, shared_ptr<const TaskScheduler::Job> job, Reflection::ValueArray* result)
  3507. {
  3508. if (job->getArbiter().get()!=dataModel)
  3509. return;
  3510.  
  3511.  
  3512. shared_ptr<Reflection::ValueArray> info(rbx::make_shared<Reflection::ValueArray>());
  3513. info->push_back(job->name);
  3514. info->push_back(job->averageDutyCycle());
  3515. info->push_back(job->averageStepsPerSecond());
  3516. info->push_back(job->averageStepTime());
  3517. info->push_back(job->averageError());
  3518. info->push_back(job->isRunning());
  3519.  
  3520. result->push_back(shared_ptr<const RBX::Reflection::ValueArray>(info));
  3521. }
  3522.  
  3523. shared_ptr<const Reflection::ValueArray> DataModel::getJobsInfo()
  3524. {
  3525. // Returns some nice diagnostic information
  3526. shared_ptr<Reflection::ValueArray> result(rbx::make_shared<Reflection::ValueArray>());
  3527. {
  3528. shared_ptr<Reflection::ValueArray> info(rbx::make_shared<Reflection::ValueArray>());
  3529. info->push_back(std::string("name"));
  3530. info->push_back(std::string("averageDutyCycle"));
  3531. info->push_back(std::string("averageStepsPerSecond"));
  3532. info->push_back(std::string("averageStepTime"));
  3533. info->push_back(std::string("averageError"));
  3534. info->push_back(std::string("isRunning"));
  3535.  
  3536. result->push_back(shared_ptr<const RBX::Reflection::ValueArray>(info));
  3537. }
  3538. std::vector<boost::shared_ptr<const TaskScheduler::Job> > jobs;
  3539. TaskScheduler::singleton().getJobsInfo(jobs);
  3540. std::for_each(jobs.begin(), jobs.end(), boost::bind(appendJobInfo, this, _1, result.get()));
  3541. return result;
  3542. }
  3543.  
  3544. void DataModel::setCreatorID(int creatorID, CreatorType creatorType)
  3545. {
  3546. if(this->creatorID != creatorID)
  3547. {
  3548. this->creatorID = creatorID;
  3549. raisePropertyChanged(prop_creatorId);
  3550. }
  3551. if(this->creatorType != creatorType)
  3552. {
  3553. this->creatorType = creatorType;
  3554. raisePropertyChanged(prop_creatorType);
  3555. }
  3556. }
  3557.  
  3558. int DataModel::getPlaceIDOrZeroInStudio()
  3559. {
  3560. return runningInStudio ? 0 : getPlaceID();
  3561. }
  3562.  
  3563. static void updateFlagsOnPlaceFilter(const std::string& name, const std::string& varValue, void* context)
  3564. {
  3565. if (!varValue.empty())
  3566. {
  3567. std::string prefix = "PlaceFilter_";
  3568. std::size_t offset = name.find(prefix.c_str());
  3569. if (offset != std::string::npos)
  3570. {
  3571. offset += prefix.length();
  3572. DataModel* dm = reinterpret_cast<DataModel*>(context);
  3573. std::string flagName = name.substr(offset);
  3574. std::vector<std::string> places;
  3575. boost::split(places, varValue, boost::is_any_of(";"));
  3576. for (std::size_t i=1; i<places.size(); i++)
  3577. {
  3578. std::string placeIdStr = places[i];
  3579. if (dm->getPlaceID() == atoi(placeIdStr.c_str()))
  3580. {
  3581. FLog::SetValue(flagName, places[0] /*value*/, FASTVARTYPE_ANY, true);
  3582. break;
  3583. }
  3584. }
  3585. }
  3586. }
  3587. }
  3588.  
  3589. void DataModel::setPlaceID(int placeID, bool robloxPlace)
  3590. {
  3591. FASTLOG1(FLog::CloseDataModel, "Setting place ID %u", placeID);
  3592. RbxDbgInfo::AddPlace(placeID);
  3593.  
  3594. if(this->placeID != placeID)
  3595. {
  3596. this->placeID = placeID;
  3597.  
  3598. FLog::ForEachVariable(&updateFlagsOnPlaceFilter, this, FASTVARTYPE_ANY);
  3599.  
  3600. raisePropertyChanged(prop_placeId);
  3601. }
  3602.  
  3603. if(ScriptContext* sc = create<ScriptContext>())
  3604. sc->setRobloxPlace(robloxPlace);
  3605.  
  3606. Http::placeID = RBX::format("%d", placeID);
  3607.  
  3608. Analytics::setPlaceId(placeID);
  3609. RobloxGoogleAnalytics::setPlaceID(placeID);
  3610. }
  3611.  
  3612.  
  3613. static void gameStartInfoLoadedHelperSuccess(weak_ptr<DataModel> dm, std::string json)
  3614. {
  3615. FASTLOG(DFLog::R15Character, "DataModel::gameStartInfoLoadedHelperSuccess");
  3616.  
  3617. shared_ptr<DataModel> dm_shared = dm.lock();
  3618. if (dm_shared.get() == NULL)
  3619. return;
  3620.  
  3621. if (json.empty())
  3622. {
  3623. dm_shared->universeDataLoaded.set_value();
  3624. dm_shared->clearUniverseDataRequested();
  3625. return;
  3626. }
  3627.  
  3628. FASTLOGS(DFLog::R15Character, "DataModel::gameStartInfoLoadedHelperSuccess %s", json.c_str());
  3629.  
  3630. RBX::Reflection::Variant v;
  3631. if (RBX::WebParser::parseJSONObject(json, v))
  3632. {
  3633. bool forceR15 = v.cast<shared_ptr<const Reflection::ValueTable> >()->at("r15Morphing").cast<bool>();
  3634. dm_shared->setForceR15(forceR15);
  3635. }
  3636.  
  3637. dm_shared->universeDataLoaded.set_value();
  3638. dm_shared->clearUniverseDataRequested();
  3639. }
  3640.  
  3641. static void gameStartInfoLoadedHelperError(weak_ptr<DataModel> dm, std::string error)
  3642. {
  3643. FASTLOG(DFLog::R15Character, "DataModel::gameStartInfoLoadedHelperError");
  3644.  
  3645. shared_ptr<DataModel> dm_shared = dm.lock();
  3646. if (dm_shared.get() == NULL)
  3647. return;
  3648.  
  3649. FASTLOGS(DFLog::R15Character, "DataModel::gameStartInfoLoadedHelperError %s", error.c_str());
  3650.  
  3651. // didn't get settings, just do nothing for now
  3652. dm_shared->universeDataLoaded.set_value();
  3653. dm_shared->clearUniverseDataRequested();
  3654. }
  3655.  
  3656. void DataModel::setForceR15(bool v)
  3657. {
  3658. if (v != forceR15)
  3659. {
  3660. forceR15 = v;
  3661. raisePropertyChanged(prop_forceR15);
  3662. }
  3663. }
  3664.  
  3665. void DataModel::setCanRequestUniverseInfo(bool value)
  3666. {
  3667. FASTLOG(DFLog::R15Character, "DataModel::requestGameStartInfo setCanRequestUniverseInfo");
  3668.  
  3669. if (value != canRequestUniverseInfo)
  3670. {
  3671. FASTLOG(DFLog::R15Character, "DataModel::requestGameStartInfo setCanRequestUniverseInfo setting");
  3672. canRequestUniverseInfo = value;
  3673. if (canRequestUniverseInfo && universeDataRequested && Network::Players::backendProcessing(this))
  3674. {
  3675. requestGameStartInfo();
  3676. }
  3677. }
  3678. }
  3679.  
  3680. void DataModel::requestGameStartInfo()
  3681. {
  3682. FASTLOG(DFLog::R15Character, "DataModel::requestGameStartInfo universeDataRequested");
  3683.  
  3684. if (RBX::HttpRbxApiService* apiService = RBX::ServiceProvider::create<RBX::HttpRbxApiService>(this))
  3685. {
  3686. apiService->getAsync( RBX::format("universes/%d/game-start-info", universeId),false, RBX::PRIORITY_DEFAULT,
  3687. boost::bind(&gameStartInfoLoadedHelperSuccess, weak_from(this), _1),
  3688. boost::bind(&gameStartInfoLoadedHelperError, weak_from(this), _1));
  3689. }
  3690. }
  3691.  
  3692.  
  3693. void DataModel::setUniverseId(int uId)
  3694. {
  3695. FASTLOG1(DFLog::R15Character, "DataModel::setUniverseId %d", uId);
  3696. if (uId != universeId)
  3697. {
  3698. universeId = uId;
  3699.  
  3700. if (DFFlag::UseR15Character && Network::Players::backendProcessing(this))
  3701. {
  3702. FASTLOG(DFLog::R15Character, "DataModel::setUniverseId universeDataRequested");
  3703. universeDataLoaded = boost::promise<void>();
  3704. universeDataRequested = true;
  3705. if (canRequestUniverseInfo)
  3706. requestGameStartInfo();
  3707. }
  3708. }
  3709. }
  3710.  
  3711. void DataModel::setIsStudio(bool runningInStudio)
  3712. {
  3713. this->runningInStudio = runningInStudio;
  3714. }
  3715.  
  3716. void DataModel::setIsRunMode(bool value)
  3717. {
  3718. this->isStudioRunMode = value;
  3719. }
  3720.  
  3721. void DataModel::setPlaceVersion(int placeVersion)
  3722. {
  3723. FASTLOG1(FLog::CloseDataModel, "Setting place version %u", placeVersion);
  3724. if(this->placeVersion != placeVersion)
  3725. {
  3726. this->placeVersion = placeVersion;
  3727. raisePropertyChanged(prop_placeVersion);
  3728. }
  3729. }
  3730. bool DataModel::isGearTypeAllowed(GearType gearType)
  3731. {
  3732. return (allowedGearTypes & (1 << gearType)) != 0;
  3733. }
  3734. void DataModel::loadPlugins()
  3735. {
  3736. throw RBX::runtime_error("load plugins not supported");
  3737. }
  3738. void DataModel::setGenre(Genre genre)
  3739. {
  3740. if(this->genre != genre)
  3741. {
  3742. this->genre = genre;
  3743. raisePropertyChanged(prop_genre);
  3744. }
  3745. }
  3746. void DataModel::setGear(GearGenreSetting gearGenreSetting, int allowedGearTypes)
  3747. {
  3748. if(this->gearGenreSetting != gearGenreSetting)
  3749. {
  3750. this->gearGenreSetting = gearGenreSetting ;
  3751. raisePropertyChanged(prop_gearGenreSetting);
  3752. }
  3753.  
  3754. if(this->allowedGearTypes != allowedGearTypes)
  3755. {
  3756. this->allowedGearTypes = allowedGearTypes;
  3757. allowedGearTypeChanged();
  3758. }
  3759. }
  3760.  
  3761. std::string DataModel::getVIPServerId() const
  3762. {
  3763. if (RBX::Network::Players::clientIsPresent(this))
  3764. {
  3765. RBX::StandardOut::singleton()->printf(MESSAGE_WARNING, "VIPServerID checked on client, but only set on server.");
  3766. }
  3767.  
  3768. return vipServerId;
  3769. }
  3770.  
  3771. void DataModel::setVIPServerId(std::string value)
  3772. {
  3773. if (value != vipServerId)
  3774. {
  3775. vipServerId = value;
  3776. raisePropertyChanged(desc_VIPServerId);
  3777. }
  3778. }
  3779.  
  3780. int DataModel::getVIPServerOwnerId() const
  3781. {
  3782. if (RBX::Network::Players::clientIsPresent(this))
  3783. {
  3784. RBX::StandardOut::singleton()->printf(MESSAGE_WARNING, "VIPServerOwnerID checked on client, but only set on server.");
  3785. }
  3786.  
  3787. return vipServerOwnerId;
  3788. }
  3789.  
  3790. void DataModel::setVIPServerOwnerId(int value)
  3791. {
  3792. if (value != vipServerOwnerId)
  3793. {
  3794. vipServerOwnerId = value;
  3795. raisePropertyChanged(desc_VIPServerOwnerId);
  3796. }
  3797. }
  3798.  
  3799. void DataModel::gameLoaded()
  3800. {
  3801. if(!getIsGameLoaded())
  3802. setIsGameLoaded(true);
  3803. }
  3804.  
  3805.  
  3806. void DataModel::setJobsExtendedStatsWindow(double seconds)
  3807. {
  3808. TaskScheduler::singleton().setJobsExtendedStatsWindow(seconds);
  3809. }
  3810.  
  3811. static void appendJobExtendedStats(DataModel* dataModel, shared_ptr<const TaskScheduler::Job> job, Reflection::ValueArray* result)
  3812. {
  3813. if (job->getArbiter().get()!=dataModel)
  3814. return;
  3815.  
  3816. shared_ptr<Reflection::ValueArray> info(rbx::make_shared<Reflection::ValueArray>());
  3817.  
  3818. WindowAverageDutyCycle<>::Stats stats = job->getDutyCycleWindow().getStats();
  3819. info->push_back(job->name);
  3820.  
  3821. info->push_back(stats.time.average);
  3822. info->push_back(stats.time.variance);
  3823. info->push_back((double)stats.time.samples);
  3824. info->push_back(stats.interval.average.seconds());
  3825. info->push_back(stats.interval.variance.seconds());
  3826. info->push_back((double)stats.interval.samples);
  3827. info->push_back(stats.dutyfraction);
  3828.  
  3829. result->push_back(shared_ptr<const RBX::Reflection::ValueArray>(info));
  3830.  
  3831. }
  3832.  
  3833. shared_ptr<const Reflection::ValueArray> DataModel::getJobsExtendedStats()
  3834. {
  3835. // Returns some nice diagnostic information
  3836. shared_ptr<Reflection::ValueArray> result(rbx::make_shared<Reflection::ValueArray>());
  3837. {
  3838.  
  3839. shared_ptr<Reflection::ValueArray> info(rbx::make_shared<Reflection::ValueArray>());
  3840. info->push_back(std::string("name"));
  3841. info->push_back(std::string("time.average"));
  3842. info->push_back(std::string("time.variance"));
  3843. info->push_back(std::string("time.samples"));
  3844. info->push_back(std::string("interval.average"));
  3845. info->push_back(std::string("interval.variance"));
  3846. info->push_back(std::string("interval.samples"));
  3847. info->push_back(std::string("dutyfraction"));
  3848.  
  3849. result->push_back(shared_ptr<const RBX::Reflection::ValueArray>(info));
  3850. }
  3851. std::vector<boost::shared_ptr<const TaskScheduler::Job> > jobs;
  3852. TaskScheduler::singleton().getJobsInfo(jobs);
  3853. std::for_each(jobs.begin(), jobs.end(), boost::bind(appendJobExtendedStats, this, _1, result.get()));
  3854. return result;
  3855. }
  3856.  
  3857. static void getJobTimePeakFractionFunc(DataModel* dataModel, shared_ptr<const TaskScheduler::Job> job, std::string& jobname, double greaterThan, double* result)
  3858. {
  3859. if (job->getArbiter().get()!=dataModel)
  3860. return;
  3861.  
  3862. const WindowAverageDutyCycle<>& data = job->getDutyCycleWindow();
  3863.  
  3864. *result = data.countTimesGreaterThan(Time::Interval(greaterThan));
  3865. *result /= data.timesamples();
  3866. }
  3867.  
  3868.  
  3869. double DataModel::getJobTimePeakFraction(std::string jobname, double greaterThan)
  3870. {
  3871. double result = -1.0;
  3872. std::vector<boost::shared_ptr<const TaskScheduler::Job> > jobs;
  3873. TaskScheduler::singleton().getJobsByName(jobname, jobs);
  3874. std::for_each(jobs.begin(), jobs.end(), boost::bind(getJobTimePeakFractionFunc, this, _1, jobname, greaterThan, &result));
  3875. return result;
  3876. }
  3877.  
  3878. static void getJobIntervalPeakFractionFunc(DataModel* dataModel, shared_ptr<const TaskScheduler::Job> job, std::string& jobname, double greaterThan, double* result)
  3879. {
  3880. if (job->getArbiter().get()!=dataModel)
  3881. return;
  3882.  
  3883. const WindowAverageDutyCycle<>& data = job->getDutyCycleWindow();
  3884.  
  3885. *result = data.countIntervalsGreaterThan(Time::Interval(greaterThan));
  3886. *result /= data.intervalsamples();
  3887. }
  3888.  
  3889.  
  3890. double DataModel::getJobIntervalPeakFraction(std::string jobname, double greaterThan)
  3891. {
  3892. double result = -1.0;
  3893. std::vector<boost::shared_ptr<const TaskScheduler::Job> > jobs;
  3894. TaskScheduler::singleton().getJobsByName(jobname, jobs);
  3895. std::for_each(jobs.begin(), jobs.end(), boost::bind(getJobIntervalPeakFractionFunc, this, _1, jobname, greaterThan, &result));
  3896. return result;
  3897. }
  3898.  
  3899. /*override*/ void DataModel::onChildChanged(Instance* instance, const PropertyChanged& event) {
  3900. RBXASSERT(write_requested==1);
  3901. Super::onChildChanged(instance, event);
  3902. this->setDirty(true);
  3903. if (!itemChangedSignal.empty())
  3904. itemChangedSignal(shared_from(instance), &event.getDescriptor());
  3905. }
  3906. /*override*/ void DataModel::onDescendantAdded(Instance* instance) {
  3907. RBXASSERT(write_requested==1);
  3908.  
  3909. // keep track of how instances this datamodel has
  3910. numInstances++;
  3911. if (Instance::fastDynamicCast<PartInstance>(instance))
  3912. numPartInstances++;
  3913.  
  3914. Super::onDescendantAdded(instance);
  3915. this->setDirty(true);
  3916. }
  3917. /*override*/ void DataModel::onDescendantRemoving(const shared_ptr<Instance>& instance) {
  3918. RBXASSERT(write_requested==1);
  3919.  
  3920. numInstances--;
  3921. if (Instance::fastDynamicCast<PartInstance>(instance.get()))
  3922. numPartInstances--;
  3923.  
  3924. Super::onDescendantRemoving(instance);
  3925. this->setDirty(true);
  3926. }
  3927.  
  3928. int DataModel::getNumPlayers() const
  3929. {
  3930. const Network::Players* players = find<Network::Players>();
  3931. if (!players)
  3932. return 0;
  3933. return
  3934. players->getNumPlayers();
  3935. }
  3936.  
  3937. void DataModel::setUiMessage(std::string message)
  3938. {
  3939. uiMessage = message;
  3940. }
  3941.  
  3942. void DataModel::toggleToolsOff()
  3943. {
  3944. Verb* verb = getWhitelistVerb("Toggle", "Play", "Mode");
  3945.  
  3946. // Only toggle if tools are enabled, so user can't use it to turn tools back on
  3947. if (verb!=NULL && verb->isChecked()){
  3948. Verb::doItWithChecks(verb, NULL);
  3949. }
  3950. }
  3951.  
  3952. void DataModel::clearUiMessage()
  3953. {
  3954. uiMessage = "";
  3955. }
  3956.  
  3957. void DataModel::setUiMessageBrickCount()
  3958. {
  3959. uiMessage = "[[[progress]]]";
  3960. }
  3961.  
  3962. void DataModel::TakeScreenshotTask(weak_ptr<RBX::DataModel> weakDataModel)
  3963. {
  3964. if (shared_ptr<RBX::DataModel> dataModel = weakDataModel.lock())
  3965. {
  3966. dataModel->screenshotSignal();
  3967. }
  3968. }
  3969.  
  3970. void DataModel::ScreenshotReadyTask(weak_ptr<RBX::DataModel> weakDataModel, const std::string &filename)
  3971. {
  3972. if (shared_ptr<RBX::DataModel> dataModel = weakDataModel.lock())
  3973. {
  3974. dataModel->screenshotReadySignal(filename);
  3975. }
  3976. }
  3977.  
  3978. void DataModel::ScreenshotUploadTask(weak_ptr<RBX::DataModel> weakDataModel, bool finished)
  3979. {
  3980. if (shared_ptr<RBX::DataModel> dataModel = weakDataModel.lock())
  3981. {
  3982. dataModel->screenshotUploadSignal(finished);
  3983. }
  3984. }
  3985.  
  3986. void DataModel::ShowMessage(weak_ptr<RBX::DataModel> weakDataModel, int slot, const std::string &message, double duration)
  3987. {
  3988. if (shared_ptr<RBX::DataModel> dataModel = weakDataModel.lock())
  3989. {
  3990. if(shared_ptr<RBX::CoreGuiService> coreGuiService = shared_from(dataModel->find<RBX::CoreGuiService>()))
  3991. {
  3992. coreGuiService->displayOnScreenMessage(slot, message, duration);
  3993. }
  3994. }
  3995. }
  3996.  
  3997. bool DataModel::currentThreadHasWriteLock() const
  3998. {
  3999. // TODO: Do we need a mutex around this?
  4000. return writeRequestingThread == GetCurrentThreadId();
  4001. }
  4002.  
  4003. bool DataModel::currentThreadHasWriteLock(Instance* context)
  4004. {
  4005. DataModel* dm = get(context);
  4006. return !dm || dm->currentThreadHasWriteLock();
  4007. }
  4008.  
  4009. Instance* DataModel::getLightingDeprecated() const
  4010. {
  4011. return ServiceProvider::find<Lighting>(this);
  4012. }
  4013.  
  4014. DataModel::scoped_write_request::scoped_write_request(Instance* context)
  4015. : dataModel(DataModel::get(context))
  4016. {
  4017. if (dataModel)
  4018. {
  4019. RBX::mutex::scoped_lock lock(dataModel->debugLock);
  4020.  
  4021. RBXASSERT(dataModel->write_requested == 0);
  4022. RBXASSERT(dataModel->read_requested == 0);
  4023. RBXASSERT(dataModel->writeRequestingThread == 0);
  4024.  
  4025. dataModel->write_requested = 1;
  4026. dataModel->writeRequestingThread = GetCurrentThreadId();
  4027. }
  4028. }
  4029.  
  4030. DataModel::scoped_write_request::~scoped_write_request()
  4031. {
  4032. if (dataModel)
  4033. {
  4034. RBX::mutex::scoped_lock lock(dataModel->debugLock);
  4035.  
  4036. RBXASSERT(dataModel->write_requested == 1);
  4037. RBXASSERT(dataModel->read_requested == 0);
  4038. RBXASSERT(dataModel->writeRequestingThread == GetCurrentThreadId());
  4039.  
  4040. dataModel->write_requested = 0;
  4041. dataModel->writeRequestingThread = 0;
  4042. }
  4043. }
  4044.  
  4045. // Place this code around tasks that write to a DataModel
  4046. DataModel::scoped_read_request::scoped_read_request(Instance* context)
  4047. : dataModel(DataModel::get(context))
  4048. {
  4049. if (dataModel)
  4050. {
  4051. RBX::mutex::scoped_lock lock(dataModel->debugLock);
  4052.  
  4053. RBXASSERT(dataModel->write_requested == 0);
  4054.  
  4055. dataModel->read_requested++;
  4056. }
  4057. }
  4058.  
  4059. DataModel::scoped_read_request::~scoped_read_request()
  4060. {
  4061. if (dataModel)
  4062. {
  4063. RBX::mutex::scoped_lock lock(dataModel->debugLock);
  4064.  
  4065. RBXASSERT(dataModel->write_requested == 0);
  4066. RBXASSERT(dataModel->read_requested > 0);
  4067.  
  4068. dataModel->read_requested--;
  4069. }
  4070. }
  4071.  
  4072. DataModel::scoped_write_transfer::scoped_write_transfer(Instance* context)
  4073. : dataModel(DataModel::get(context))
  4074. , oldWritingThread(0)
  4075. {
  4076. if (dataModel)
  4077. {
  4078. RBX::mutex::scoped_lock lock(dataModel->debugLock);
  4079.  
  4080. RBXASSERT(dataModel->write_requested == 1);
  4081. RBXASSERT(dataModel->writeRequestingThread != GetCurrentThreadId());
  4082. RBXASSERT(dataModel->writeRequestingThread != 0);
  4083.  
  4084. oldWritingThread = dataModel->writeRequestingThread;
  4085. dataModel->writeRequestingThread = GetCurrentThreadId();
  4086. }
  4087. }
  4088.  
  4089. DataModel::scoped_write_transfer::~scoped_write_transfer()
  4090. {
  4091. if (dataModel)
  4092. {
  4093. RBX::mutex::scoped_lock lock(dataModel->debugLock);
  4094.  
  4095. RBXASSERT(dataModel->write_requested == 1);
  4096. RBXASSERT(dataModel->writeRequestingThread == GetCurrentThreadId());
  4097.  
  4098. dataModel->writeRequestingThread = oldWritingThread;
  4099. }
  4100. }
  4101.  
  4102.  
  4103. unsigned int DataModel::allHackFlagsOredTogether() {
  4104. unsigned int result = 0;
  4105. #if !defined(RBX_STUDIO_BUILD)
  4106. VMProtectBeginMutation("18");
  4107. boost::mutex::scoped_lock l(hackFlagSetMutex);
  4108.  
  4109. for (boost::unordered_set<unsigned int>::iterator itr = hackFlagSet.begin();
  4110. itr != hackFlagSet.end(); ++itr) {
  4111. result |= *itr;
  4112. }
  4113. VMProtectEnd();
  4114. #endif
  4115. return result;
  4116. }
  4117.  
  4118. void DataModel::processHttpRequestResponseOnLock(DataModel *dataModel, std::string* response, std::exception* exception, boost::function<void(shared_ptr<std::string>,shared_ptr<std::exception> exception)> onLockAcquired)
  4119. {
  4120. if (dataModel == NULL)
  4121. {
  4122. return;
  4123. }
  4124.  
  4125. shared_ptr<std::string> responseCopy(response ? new std::string(*response) : NULL);
  4126. shared_ptr<std::exception> exceptionCopy(exception ? new std::exception(*exception) : NULL);
  4127. if (DFFlag::DataModelProcessHttpRequestResponseOnLockUseSubmitTask)
  4128. {
  4129. dataModel->submitTask(
  4130. boost::bind(onLockAcquired, responseCopy, exceptionCopy),
  4131. RBX::DataModelJob::TaskType::Write);
  4132. }
  4133. else
  4134. {
  4135. DataModel::LegacyLock lock(dataModel, DataModelJob::Write);
  4136. onLockAcquired(responseCopy, exceptionCopy);
  4137. }
  4138. }
  4139.  
  4140. } // namespace
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement