mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
358 lines
9.1 KiB
C++
358 lines
9.1 KiB
C++
/*
|
|
* RobloxCpp.cpp
|
|
* MacClient
|
|
*
|
|
* Created by Tony on 1/26/11.
|
|
* Copyright 2011 __MyCompanyName__. All rights reserved.
|
|
*
|
|
*/
|
|
|
|
|
|
#include "Roblox.h"
|
|
|
|
#undef max
|
|
#undef min
|
|
|
|
|
|
#include "v8datamodel/datamodel.h"
|
|
#include "v8datamodel/workspace.h"
|
|
#include "v8datamodel/partinstance.h"
|
|
|
|
#include "v8datamodel/factoryregistration.h"
|
|
#include "Util/FileSystem.h"
|
|
#include "Util/Http.h"
|
|
#include "Util/Profiling.h"
|
|
#include "Util/Statistics.h"
|
|
#include "Util/MD5Hasher.h"
|
|
#include "util/RobloxGoogleAnalytics.h"
|
|
#include "v8datamodel/game.h"
|
|
#include "v8datamodel/GameSettings.h"
|
|
#include "v8datamodel/DebugSettings.h"
|
|
#include "v8datamodel/PhysicsSettings.h"
|
|
#include "v8datamodel/ContentProvider.h"
|
|
#include "v8datamodel/GlobalSettings.h"
|
|
#include "script/ScriptContext.h"
|
|
#include "script/LuaSettings.h"
|
|
#include "network/api.h"
|
|
|
|
#include "rbx/ProcessPerfCounter.h"
|
|
#include "rbx/Profiler.h"
|
|
|
|
#include "Gui/ProfanityFilter.h"
|
|
|
|
#include "boost/filesystem.hpp"
|
|
|
|
#include "MachineConfiguration.h"
|
|
#include <string>
|
|
|
|
FASTFLAG(GoogleAnalyticsTrackingEnabled)
|
|
DYNAMIC_LOGGROUP(GoogleAnalyticsTracking)
|
|
LOGGROUP(Network)
|
|
LOGGROUP(PlayerShutdownLuaTimeoutSeconds)
|
|
|
|
bool Roblox::initialized = false;
|
|
std::string Roblox::assetFolder;
|
|
Roblox::RunMode Roblox::runMode = Roblox::RUN_FILE;
|
|
rbx::signals::scoped_connection messageOutConnection;
|
|
|
|
RBX::ClientAppSettings Roblox::appSettings;
|
|
|
|
static boost::shared_ptr<RBX::Game> preloadedGame;
|
|
static boost::mutex preloadedGameMutex;
|
|
static bool preloadShuttingDown = false;
|
|
|
|
static boost::thread releaseGameThread;
|
|
|
|
extern "C" {
|
|
void writeFastLogDumpHelper(const char *fileName, int numEntries)
|
|
{
|
|
FLog::WriteFastLogDump(fileName, numEntries);
|
|
}
|
|
};
|
|
|
|
|
|
|
|
static void do_preloadGame(bool isApp)
|
|
{
|
|
// Avoid having 2 DataModels open at once
|
|
// It should be safe, but may as well not play with fire
|
|
releaseGameThread.join();
|
|
|
|
boost::mutex::scoped_lock lock(preloadedGameMutex);
|
|
if (preloadShuttingDown)
|
|
return;
|
|
if (!preloadedGame)
|
|
{
|
|
RBX::Time start = RBX::Time::now<RBX::Time::Fast>();
|
|
preloadedGame.reset(new RBX::SecurePlayerGame(NULL, ::GetBaseURL().c_str()));
|
|
RBX::Time stop = RBX::Time::now<RBX::Time::Fast>();
|
|
double secs = (stop - start).seconds();
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_OUTPUT, "Preloaded Game %gsec", secs);
|
|
}
|
|
else
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_OUTPUT, "Already preloaded Game");
|
|
}
|
|
|
|
void Roblox::preloadGame(bool isApp)
|
|
{
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_OUTPUT, "Requesting preload Game");
|
|
boost::thread(boost::bind(&do_preloadGame, isApp));
|
|
}
|
|
|
|
extern std::string macBundlePath();
|
|
|
|
bool Roblox::globalInit(bool isApp)
|
|
{
|
|
if (initialized)
|
|
{
|
|
return true;
|
|
}
|
|
RBX::Game::globalInit(false);
|
|
|
|
std::string fileRobloxPlayer = macBundlePath() + "/Contents/MacOS/RobloxPlayer";
|
|
|
|
// hash may have been overridden on command line, otherwise calculate from executable
|
|
if (RBX::DataModel::hash.length() == 0)
|
|
{
|
|
RBX::DataModel::hash = RBX::CollectMd5Hash(fileRobloxPlayer);
|
|
}
|
|
|
|
RBX::ContentProvider::setAssetFolder(assetFolder.c_str());
|
|
|
|
// Reset synchronized flags, they should be set by the server
|
|
FLog::ResetSynchronizedVariablesState();
|
|
|
|
{
|
|
bool useCurl = rand() % 100 < RBX::ClientAppSettings::singleton().GetValueHttpUseCurlPercentageMacClient();
|
|
FASTLOG1(FLog::Network, "Use CURL = %d", useCurl);
|
|
RBX::Http::SetUseCurl(useCurl);
|
|
|
|
RBX::Http::SetUseStatistics(true);
|
|
}
|
|
|
|
{
|
|
int lottery = rand() % 100;
|
|
FASTLOG1(DFLog::GoogleAnalyticsTracking, "Google analytics lottery number = %d", lottery);
|
|
// initialize google analytics
|
|
if (FFlag::GoogleAnalyticsTrackingEnabled && (lottery < RBX::ClientAppSettings::singleton().GetValueGoogleAnalyticsLoadPlayer()))
|
|
{
|
|
RBX::RobloxGoogleAnalytics::setCanUseAnalytics();
|
|
|
|
RBX::RobloxGoogleAnalytics::init(RBX::ClientAppSettings::singleton().GetValueGoogleAnalyticsAccountPropertyIDPlayer(),
|
|
RBX::ClientAppSettings::singleton().GetValueGoogleAnalyticsThreadPoolMaxScheduleSize());
|
|
}
|
|
}
|
|
|
|
// must be after globalInit, where http pool is initialized
|
|
RBX::postMachineConfiguration(::GetBaseURL().c_str(), 0);
|
|
|
|
messageOutConnection = RBX::StandardOut::singleton()->messageOut.connect(&onMessageOut);
|
|
|
|
RBX::GlobalAdvancedSettings::singleton()->loadState("");
|
|
{
|
|
RBX::Security::Impersonator impersonate(RBX::Security::RobloxGameScript_);
|
|
RBX::GlobalBasicSettings::singleton()->loadState("");
|
|
}
|
|
|
|
RBX::Profiler::onThreadCreate("Main");
|
|
|
|
// Initialize the TaskScheduler (after loading configs)
|
|
RBX::TaskScheduler::singleton().setThreadCount(RBX::TaskSchedulerSettings::singleton().getThreadPoolConfig());
|
|
|
|
initialized = true;
|
|
|
|
preloadGame(isApp);
|
|
|
|
return true;
|
|
}
|
|
|
|
boost::shared_ptr<RBX::Game> Roblox::getpreloadedGame(const bool isApp)
|
|
{
|
|
boost::mutex::scoped_lock lock(preloadedGameMutex);
|
|
if (!preloadedGame)
|
|
{
|
|
RBX::Time start = RBX::Time::now<RBX::Time::Fast>();
|
|
preloadedGame.reset(new RBX::SecurePlayerGame(NULL, ::GetBaseURL().c_str()));
|
|
RBX::Time stop = RBX::Time::now<RBX::Time::Fast>();
|
|
double secs = (stop - start).seconds();
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_OUTPUT, "Loaded Game %gsec", secs);
|
|
}
|
|
boost::shared_ptr<RBX::Game> temp = preloadedGame;
|
|
preloadedGame.reset();
|
|
|
|
if (FLog::PlayerShutdownLuaTimeoutSeconds > 0)
|
|
temp->getDataModel()->create<RBX::ScriptContext>();
|
|
|
|
return temp;
|
|
}
|
|
|
|
|
|
|
|
static void releaseGame(boost::shared_ptr<RBX::Game> game)
|
|
{
|
|
RBX::GlobalBasicSettings::singleton()->saveState();
|
|
game->shutdown();
|
|
}
|
|
|
|
void Roblox::relinquishGame(boost::shared_ptr<RBX::Game>& game)
|
|
{
|
|
releaseGameThread.join();
|
|
|
|
// Defer deletion of DataModel for later (so that we don't block)
|
|
releaseGameThread = boost::thread(boost::bind(&releaseGame, game));
|
|
|
|
game.reset();
|
|
}
|
|
|
|
|
|
void Roblox::globalShutdown()
|
|
{
|
|
{
|
|
boost::mutex::scoped_lock lock(preloadedGameMutex);
|
|
preloadShuttingDown = true;
|
|
if (preloadedGame)
|
|
preloadedGame->shutdown(); // Don't rely on destructor, in case somebody else holds a reference
|
|
preloadedGame.reset();
|
|
}
|
|
|
|
// Don't exit until Game has been shut down. Otherwise we might get crashes in static destructors
|
|
releaseGameThread.join();
|
|
|
|
if (!initialized)
|
|
return;
|
|
|
|
RBX::Game::globalExit();
|
|
messageOutConnection.disconnect();
|
|
}
|
|
|
|
|
|
void doCrash()
|
|
{
|
|
RBXCRASH();
|
|
}
|
|
|
|
void Roblox::testCrash()
|
|
{
|
|
boost::shared_ptr<RBX::Instance> i = RBX::Creatable<RBX::Instance>::create<RBX::PartInstance>();
|
|
i->propertyChangedSignal.connect(boost::bind(&doCrash));
|
|
i->setName("blah");
|
|
|
|
}
|
|
|
|
void Roblox::setArgs(const char *gameFolder, const char *runMode)
|
|
{
|
|
Roblox::assetFolder = std::string(gameFolder) + "/content/";
|
|
|
|
if (strcmp(runMode,"c") == 0){
|
|
Roblox::runMode = RUN_CLIENT;
|
|
}
|
|
else if (strcmp(runMode,"s") == 0){
|
|
Roblox::runMode = RUN_SERVER;
|
|
}
|
|
else {
|
|
Roblox::runMode = RUN_FILE;
|
|
}
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
void setRobloxArgs(const char *gameFolder, const char *runMode)
|
|
{
|
|
Roblox::setArgs(gameFolder, runMode);
|
|
}
|
|
}
|
|
|
|
// Utility functions
|
|
|
|
void Roblox::onMessageOut(const RBX::StandardOutMessage& message)
|
|
{
|
|
switch (message.type)
|
|
{
|
|
case RBX::MESSAGE_INFO:
|
|
printf("INFO: %s\n", message.message.c_str());
|
|
break;
|
|
case RBX::MESSAGE_WARNING:
|
|
printf("WARNING: %s\n", message.message.c_str());
|
|
break;
|
|
case RBX::MESSAGE_ERROR:
|
|
printf("ERROR: %s\n", message.message.c_str());
|
|
break;
|
|
default:
|
|
printf("%s\n", message.message.c_str());
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void handler(std::exception* ex, std::string file)
|
|
{
|
|
if (ex)
|
|
return;
|
|
std::remove(file.c_str());
|
|
}
|
|
|
|
void uploadAndDeletFileAsync(const char* url, const char* file)
|
|
{
|
|
boost::shared_ptr<std::fstream> data(new std::fstream(file, std::ios_base::in | std::ios_base::binary));
|
|
size_t begin = data->tellg();
|
|
data->seekg (0, std::ios::end);
|
|
size_t end = data->tellg();
|
|
if (end > begin)
|
|
{
|
|
data->seekg (0, std::ios::beg);
|
|
|
|
std::string version;
|
|
while (*data)
|
|
{
|
|
char buff[255];
|
|
data->getline(buff, 155);
|
|
std::string line = buff;
|
|
|
|
if (line.substr(0, 8) == "Version:")
|
|
{
|
|
// "Version: 0.34.0.107 (107)"
|
|
for (size_t i = 8; i < line.size(); ++i)
|
|
if (line[i] != ' ')
|
|
{
|
|
line = line.substr(i);
|
|
break;
|
|
}
|
|
|
|
// "0.34.0.107 (107)"
|
|
for (size_t i = 0; i < line.size(); ++i)
|
|
if (line[i] == ' ')
|
|
{
|
|
line = line.substr(0, i);
|
|
// "0.34.0.107"
|
|
while (true)
|
|
{
|
|
int j = line.find('.');
|
|
if (j == std::string::npos)
|
|
break;
|
|
line = line.replace(j, 1, ",%20");
|
|
}
|
|
// "0,%2034,%200,%20107"
|
|
version = line;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
data->seekg (0, std::ios::beg);
|
|
|
|
boost::filesystem::path p(file);
|
|
std::string filename = "log_";
|
|
// extract file "guid"
|
|
filename += p.filename().string().substr(13, 17);
|
|
filename += "%20";
|
|
filename += version;
|
|
filename += ".crash";
|
|
|
|
std::string fullUrl = url;
|
|
fullUrl += "?filename=" + filename;
|
|
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_OUTPUT, "Uploading %s", fullUrl.c_str());
|
|
RBX::Http(fullUrl).post(data, RBX::Http::kContentTypeUrlEncoded, true, boost::bind(handler, _2, p.string()));
|
|
}
|
|
}
|