mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
1324 lines
40 KiB
C++
1324 lines
40 KiB
C++
// BootstrapperClient.cpp : Defines the entry point for the application.
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "BootstrapperClient.h"
|
|
#include "ClientInstallerSettings.h"
|
|
#include <comdef.h>
|
|
#include <taskschd.h>
|
|
#include <mstask.h>
|
|
#include <boost/function.hpp>
|
|
#include <ostream>
|
|
#include <fstream>
|
|
#include <strstream>
|
|
#include <boost/bind.hpp>
|
|
#include "SettingsLoader.h"
|
|
#include <string>
|
|
#include "HttpTools.h"
|
|
#include "ProcessInformation.h"
|
|
#include "shellapi.h"
|
|
#include <iphlpapi.h>
|
|
#include "GoogleAnalyticsHelper.h"
|
|
#include "VersionInfo.h"
|
|
#include "StringConv.h"
|
|
#include "ClientProgressDialog.h"
|
|
#include "RobloxServicesTools.h"
|
|
|
|
static const TCHAR* BootstrapperFileName = _T("RobloxPlayerLauncher.exe");
|
|
static const TCHAR* RobloxAppFileName = _T(PLAYEREXENAME);
|
|
static const TCHAR* BootstrapperMutexName = _T("www.watrbx.wtf/bootstrapper");
|
|
static const TCHAR* StartRobloxAppMutex = _T("www.watrbx.wtf/startRobloxApp");
|
|
static const TCHAR* LauncherFileName = _T("RobloxProxy.dll");
|
|
static const TCHAR* LauncherFileName64 = _T("RobloxProxy64.dll");
|
|
static const TCHAR* FriendlyName = _T("watrbx");
|
|
static const TCHAR* CLSID_Launcher = _T("{76D50904-6780-4c8b-8986-1A7EE0B1716D}");
|
|
static const TCHAR* CLSID_Launcher64 = _T("{DEE03C2B-0C0C-41A9-9877-FD4B4D7B6EA3}");
|
|
static const TCHAR* AppID_Launcher = _T("{664B192B-D17A-4921-ABF9-C6F6264E5110}");
|
|
|
|
LPCWSTR robloxUpdaterTaskName = L"RobloxGameUpdater";
|
|
LPCWSTR bgCommand = L"-qbg";
|
|
|
|
static Bootstrapper* newBootstrapper(HINSTANCE hInstance)
|
|
{
|
|
return new BootstrapperClient(hInstance);
|
|
}
|
|
int APIENTRY _tWinMain(HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPTSTR lpCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
if(0 == lstrcmp(lpCmdLine, _T("--version")))
|
|
{
|
|
CVersionInfo vi;
|
|
vi.Load(_AtlBaseModule.m_hInst);
|
|
std::string versionNumber = vi.GetFileVersionAsDotString();
|
|
MessageBoxA(NULL, versionNumber.c_str(), "Version", MB_OK);
|
|
return 0;
|
|
}
|
|
UNREFERENCED_PARAMETER(hPrevInstance);
|
|
return BootstrapperMain(hInstance,nCmdShow,&newBootstrapper);
|
|
}
|
|
|
|
BootstrapperClient::BootstrapperClient(HINSTANCE hInstance)
|
|
:Bootstrapper(hInstance)
|
|
{
|
|
LOG_ENTRY("BootstrapperClient::BootstrapperClient");
|
|
|
|
_protocolHandlerScheme = getPlayerProtocolScheme(BaseHost());
|
|
|
|
//Plugin depends on RobloxReg value as well,
|
|
//so if you ever change this guy make sure that plugins code is updates as well
|
|
_regSubPath = _T("RobloxReg");
|
|
_regPath = _T("SOFTWARE\\") + _regSubPath;
|
|
_versionFileName = _T("RobloxVersion.txt");
|
|
_versionGuidName = _T(VERSIONGUIDNAMEPLAYER);
|
|
|
|
SYSTEMTIME sysTime = {0};
|
|
|
|
GetSystemTime(&sysTime);
|
|
startHour = 10; //lets schedule it for 10AM for now
|
|
|
|
startYear = sysTime.wYear;
|
|
endYear = startYear + 1;
|
|
endMonth = startMonth = sysTime.wMonth;
|
|
endDay = startDay = sysTime.wDay;
|
|
// lets handle leap year
|
|
if (startMonth == 2 && startDay == 29)
|
|
{
|
|
endDay = 28;
|
|
}
|
|
}
|
|
|
|
std::wstring BootstrapperClient::GetRegistryPath() const
|
|
{
|
|
return _regPath;
|
|
}
|
|
|
|
std::wstring BootstrapperClient::GetRegistrySubPath() const
|
|
{
|
|
return _regSubPath;
|
|
}
|
|
|
|
const wchar_t* BootstrapperClient::GetProtocolHandlerUrlScheme() const
|
|
{
|
|
return _protocolHandlerScheme.c_str();
|
|
}
|
|
|
|
const wchar_t *BootstrapperClient::GetVersionFileName() const
|
|
{
|
|
return _versionFileName.c_str();
|
|
}
|
|
|
|
const wchar_t *BootstrapperClient::GetGuidName() const
|
|
{
|
|
return _versionGuidName.c_str();
|
|
}
|
|
|
|
const wchar_t *BootstrapperClient::GetStartAppMutexName() const
|
|
{
|
|
return StartRobloxAppMutex;
|
|
}
|
|
|
|
const wchar_t *BootstrapperClient::GetBootstrapperMutexName() const
|
|
{
|
|
return BootstrapperMutexName;
|
|
}
|
|
|
|
const wchar_t* BootstrapperClient::GetFriendlyName() const
|
|
{
|
|
return FriendlyName;
|
|
}
|
|
|
|
std::wstring BootstrapperClient::GetProductCodeKey() const
|
|
{
|
|
return getPlayerInstallKey();
|
|
}
|
|
|
|
std::string BootstrapperClient::GetExpectedExeVersion()const
|
|
{
|
|
return settings.GetValueExeVersion();
|
|
}
|
|
|
|
void BootstrapperClient::LoadSettings()
|
|
{
|
|
try
|
|
{
|
|
SettingsLoader loader(BaseHost());
|
|
settings.ReadFromStream(loader.GetSettingsString("WindowsBootstrapperSettings").c_str());
|
|
|
|
HttpTools::httpBoostPostTimeout = settings.GetValueHttpBoostPostTimeout();
|
|
}
|
|
catch (std::exception e)
|
|
{
|
|
}
|
|
}
|
|
|
|
void BootstrapperClient::SetupGoogleAnalytics()
|
|
{
|
|
GoogleAnalyticsHelper::init(settings.GetValueGoogleAnalyticsAccountPropertyID());
|
|
}
|
|
|
|
bool BootstrapperClient::ValidateInstalledExeVersion()
|
|
{
|
|
return settings.GetValueValidateInstalledExeVersion();
|
|
}
|
|
|
|
bool BootstrapperClient::IsInstalledVersionUptodate()
|
|
{
|
|
std::string expectedVersion = GetExpectedExeVersion();
|
|
LOG_ENTRY1("Expected client version: %s", expectedVersion.c_str());
|
|
|
|
if (!expectedVersion.empty())
|
|
{
|
|
CVersionInfo vi;
|
|
std::wstring filePath = programDirectory() + GetRobloxAppFileName();
|
|
if (vi.Load(filePath))
|
|
{
|
|
std::string fileVersion = vi.GetFileVersionAsDotString();
|
|
if (expectedVersion != fileVersion)
|
|
{
|
|
LOG_ENTRY2("WARNING: RobloxApp.exe version %s != %s", expectedVersion.c_str(), fileVersion.c_str());
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
std::string BootstrapperClient::BuildDateTime(int y, int m, int d)
|
|
{
|
|
std::string sm = m < 10 ? (format_string("0%d", m)) : (format_string("%d", m));
|
|
std::string sd = d < 10 ? (format_string("0%d", d)) : (format_string("%d", d));
|
|
|
|
return format_string("%d-%s-%sT%d:00:00", y, sm.c_str(), sd.c_str(), startHour);
|
|
}
|
|
|
|
HRESULT BootstrapperClient::SheduleTaskWinVista()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<ITaskService> service;
|
|
hr = CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (void**)&service);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
hr = service->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<ITaskFolder> rootFolder;
|
|
hr = service->GetFolder(_bstr_t( L"\\"), &rootFolder);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
rootFolder->DeleteTask(_bstr_t(robloxUpdaterTaskName), 0);
|
|
CComPtr<ITaskDefinition> task;
|
|
hr = service->NewTask(0, &task);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IRegistrationInfo> regInfo;
|
|
hr = task->get_RegistrationInfo(®Info);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
TCHAR name[MAX_PATH];
|
|
DWORD bufSize = MAX_PATH;
|
|
GetUserName(name, &bufSize);
|
|
|
|
regInfo->put_Author(name);
|
|
regInfo->put_Description(L"This tasks helps Roblox game be updated.");
|
|
|
|
CComPtr<ITriggerCollection> triggerCollection;
|
|
hr = task->get_Triggers(&triggerCollection);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<ITrigger> trigger;
|
|
hr = triggerCollection->Create(TASK_TRIGGER_DAILY, &trigger);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IDailyTrigger> dailyTrigger;
|
|
hr = trigger->QueryInterface(IID_IDailyTrigger, (void**)&dailyTrigger);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = dailyTrigger->put_Id(_bstr_t(L"RBXTrigger"));
|
|
|
|
hr = dailyTrigger->put_StartBoundary(_bstr_t(CA2W(BuildDateTime(startYear, startMonth, startDay).c_str())));
|
|
hr = dailyTrigger->put_EndBoundary(_bstr_t(CA2W(BuildDateTime(endYear, endMonth, endDay).c_str())));
|
|
|
|
hr = dailyTrigger->put_DaysInterval((short)1);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IActionCollection> actionCollection;
|
|
hr = task->get_Actions(&actionCollection);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IAction> action;
|
|
hr = actionCollection->Create(TASK_ACTION_EXEC, &action);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IExecAction> execAction;
|
|
hr = action->QueryInterface(IID_IExecAction, (void**)&execAction);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = execAction->put_Path(_bstr_t(GetSelfName().c_str()));
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = execAction->put_Arguments(_bstr_t(bgCommand));
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IRegisteredTask> registeredTask;
|
|
hr = rootFolder->RegisterTaskDefinition(_bstr_t(robloxUpdaterTaskName), task, TASK_CREATE_OR_UPDATE, _variant_t(L""), _variant_t(L""),
|
|
TASK_LOGON_INTERACTIVE_TOKEN, _variant_t(L""), ®isteredTask);
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT BootstrapperClient::SheduleTaskWinXP()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CComPtr<ITaskScheduler> scheduler;
|
|
hr = CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskScheduler, (void **)&scheduler);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = scheduler->Delete(robloxUpdaterTaskName);
|
|
|
|
CComPtr<ITask> task;
|
|
hr = scheduler->NewWorkItem(robloxUpdaterTaskName, CLSID_CTask, IID_ITask, (IUnknown**)&task);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IScheduledWorkItem> item;
|
|
hr = task->QueryInterface(IID_IScheduledWorkItem, (void**)&item);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
WORD index = 0;
|
|
CComPtr<ITaskTrigger> trigger;
|
|
hr = item->CreateTrigger(&index, &trigger);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
TASK_TRIGGER triggerInfo = {0};
|
|
triggerInfo.wBeginDay = startDay;
|
|
triggerInfo.wBeginMonth = startMonth;
|
|
triggerInfo.wBeginYear = startYear;
|
|
triggerInfo.wEndDay = endDay;
|
|
triggerInfo.wEndMonth = endMonth;
|
|
triggerInfo.wEndYear = endYear;
|
|
triggerInfo.cbTriggerSize = sizeof(TASK_TRIGGER);
|
|
triggerInfo.wStartHour = startHour;
|
|
triggerInfo.rgFlags = TASK_TRIGGER_FLAG_HAS_END_DATE;
|
|
triggerInfo.TriggerType = TASK_TIME_TRIGGER_DAILY;
|
|
triggerInfo.Type.Daily.DaysInterval = 1;
|
|
|
|
hr = trigger->SetTrigger(&triggerInfo);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = task->SetApplicationName(GetSelfName().c_str());
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = task->SetParameters(bgCommand);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = item->SetFlags(TASK_FLAG_RUN_ONLY_IF_LOGGED_ON);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
TCHAR name[MAX_PATH];
|
|
DWORD bufSize = MAX_PATH;
|
|
GetUserName(name, &bufSize);
|
|
|
|
hr = item->SetAccountInformation(name, NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<IPersistFile> persistFile;
|
|
hr = task->QueryInterface(IID_IPersistFile, (void **)&persistFile);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = persistFile->Save(NULL, TRUE);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT BootstrapperClient::SheduleRobloxUpdater()
|
|
{
|
|
if (!UpdaterActive())
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
OSVERSIONINFO osver = {0};
|
|
|
|
osver.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
|
|
::GetVersionEx(&osver);
|
|
|
|
if (osver.dwMajorVersion == 5)
|
|
{
|
|
return SheduleTaskWinXP();
|
|
}
|
|
else if (osver.dwMajorVersion >= 6)
|
|
{
|
|
return SheduleTaskWinVista();
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT BootstrapperClient::UninstallRobloxUpdater()
|
|
{
|
|
OSVERSIONINFO osver = {0};
|
|
HRESULT hr = S_OK;
|
|
|
|
osver.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
|
|
::GetVersionEx(&osver);
|
|
|
|
if (osver.dwMajorVersion == 5)
|
|
{
|
|
CComPtr<ITaskScheduler> scheduler;
|
|
hr = CoCreateInstance(CLSID_CTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskScheduler, (void **)&scheduler);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = scheduler->Delete(robloxUpdaterTaskName);
|
|
}
|
|
else if (osver.dwMajorVersion >= 6)
|
|
{
|
|
|
|
CComPtr<ITaskService> service;
|
|
hr = CoCreateInstance(CLSID_TaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskService, (void**)&service);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
hr = service->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
CComPtr<ITaskFolder> rootFolder;
|
|
hr = service->GetFolder(_bstr_t( L"\\"), &rootFolder);
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = rootFolder->DeleteTask(_bstr_t(robloxUpdaterTaskName), 0);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
bool BootstrapperClient::NeedPreDeployRun()
|
|
{
|
|
if (!IsPreDeployOn())
|
|
{
|
|
LOG_ENTRY("NeedPreDeployRun = false");
|
|
return false;
|
|
}
|
|
|
|
CRegKey key;
|
|
if (SUCCEEDED(key.Open(HKEY_CURRENT_USER, _T("Software\\ROBLOX Corporation\\Roblox"), KEY_READ)))
|
|
{
|
|
TCHAR buf[MAX_PATH];
|
|
ULONG bufSize = MAX_PATH;
|
|
if (SUCCEEDED(key.QueryStringValue(_T("LastPreVersion"), buf, &bufSize)))
|
|
{
|
|
std::string preVersion(settings.GetValuePreVersion());
|
|
LOG_ENTRY2("Cure PreVersion = %s, LastPreVersion = %S", preVersion.c_str(), buf);
|
|
if (!preVersion.empty() && preVersion != convert_w2s(buf))
|
|
{
|
|
LOG_ENTRY("NeedPreDeployRun = true");
|
|
return true;
|
|
}
|
|
|
|
LOG_ENTRY("NeedPreDeployRun = false");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
LOG_ENTRY("NeedPreDeployRun = true");
|
|
return true;
|
|
}
|
|
|
|
bool BootstrapperClient::IsPreDeployOn()
|
|
{
|
|
return settings.GetValueIsPreDeployOn();
|
|
}
|
|
|
|
void BootstrapperClient::RunPreDeploy()
|
|
{
|
|
std::string preVersion = settings.GetValuePreVersion();
|
|
if (!IsPreDeployOn() || preVersion.empty() || preVersion == queryInstalledVersion())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!NeedPreDeployRun())
|
|
{
|
|
return;
|
|
}
|
|
|
|
std::string version = InstallVersion();
|
|
try
|
|
{
|
|
setInstallVersion(preVersion);
|
|
|
|
deploySelf(false);
|
|
deployRobloxProxy(false);
|
|
deployNPRobloxProxy(false);
|
|
DeployComponents(true, false);
|
|
|
|
CRegKey key;
|
|
if (SUCCEEDED(key.Create(HKEY_CURRENT_USER, _T("Software\\ROBLOX Corporation\\Roblox"))))
|
|
{
|
|
key.SetStringValue(_T("LastPreVersion"), convert_s2w(preVersion).c_str());
|
|
LOG_ENTRY("Setting last pre deploy version entry");
|
|
}
|
|
LOG_ENTRY1("Pre deployed %s version", preVersion.c_str());
|
|
}
|
|
catch (std::exception e)
|
|
{
|
|
setInstallVersion(version);
|
|
}
|
|
setInstallVersion(version);
|
|
}
|
|
|
|
std::wstring BootstrapperClient::GetRobloxAppFileName() const
|
|
{
|
|
return std::wstring(RobloxAppFileName);
|
|
}
|
|
|
|
std::wstring BootstrapperClient::GetBootstrapperFileName() const
|
|
{
|
|
return std::wstring(BootstrapperFileName);
|
|
}
|
|
|
|
static std::wstring getValue(const std::map<std::wstring, std::wstring>& argMap, const std::wstring& key)
|
|
{
|
|
std::map<std::wstring, std::wstring>::const_iterator iter = argMap.find(key);
|
|
if (iter != argMap.end())
|
|
return iter->second;
|
|
|
|
return _T("");
|
|
}
|
|
|
|
bool BootstrapperClient::ProcessProtocolHandlerArgs(const std::map<std::wstring, std::wstring>& argMap)
|
|
{
|
|
std::wstring launchMode = getValue(argMap, _T("launchmode"));
|
|
if (launchMode == _T("play"))
|
|
{
|
|
robloxAppArgs = _T("play");
|
|
playArgs.reset(new PlayArgs());
|
|
|
|
playArgs->launchMode = SharedLauncher::Play;
|
|
LOG_ENTRY("BootstrapperClient::ProcessProtocolHandlerArgs - option: play");
|
|
|
|
std::wstring placeLauncherUrl = getValue(argMap, _T("placelauncherurl"));
|
|
if (placeLauncherUrl.length() > 0)
|
|
{
|
|
TCHAR decodedUrl [1024];
|
|
memset (decodedUrl, 0, sizeof (decodedUrl));
|
|
DWORD dwUrlLength;
|
|
ATL::AtlUnescapeUrl(placeLauncherUrl.c_str(), decodedUrl, &dwUrlLength, 1024);
|
|
|
|
playArgs->script = convert_w2s(decodedUrl);
|
|
LOG_ENTRY1("BootstrapperClient::ProcessProtocolHandlerArgs = PlaceLauncherUrl, value = %s", playArgs->script.c_str());
|
|
}
|
|
|
|
playArgs->authUrl = "https://" + BaseHost() + "/Login/Negotiate.ashx";
|
|
LOG_ENTRY1("BootstrapperClient::ProcessProtocolHandlerArgs paramIndex = AuthUrl, value = %s", playArgs->authUrl.c_str());
|
|
|
|
std::wstring authTicket = getValue(argMap, _T("gameinfo"));
|
|
if (authTicket.length() > 0)
|
|
{
|
|
playArgs->authTicket = convert_w2s(authTicket);
|
|
LOG_ENTRY1("BootstrapperClient::ProcessProtocolHandlerArgs = gameInfo, value = %s", playArgs->authTicket.c_str());
|
|
}
|
|
|
|
if (BrowserTrackerId().length() > 0)
|
|
{
|
|
playArgs->browserTrackerId = BrowserTrackerId();
|
|
LOG_ENTRY1("BootstrapperClient::ProcessProtocolHandlerArgs = browserTrackerId, value = %s", playArgs->browserTrackerId.c_str());
|
|
}
|
|
}
|
|
else if (launchMode == _T("unhide"))
|
|
{
|
|
// TODO:
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool BootstrapperClient::ProcessArg(wchar_t** args, int &pos, int count)
|
|
{
|
|
if (CAtlModule::WordCmpI(args[pos], _T("-play"))==0)
|
|
{
|
|
LOG_ENTRY("BootstrapperClient::ProcessArg - started");
|
|
RegisterEvent(_T("BootstrapperPlayStarted"));
|
|
robloxAppArgs = _T("play");
|
|
playArgs.reset(new PlayArgs());
|
|
|
|
// first, lets set the kind of play we are dealing with
|
|
playArgs->launchMode = SharedLauncher::Play;
|
|
LOG_ENTRY("BootstrapperClient::ProcessArg - option: play");
|
|
|
|
if (++pos >= count)
|
|
throw std::runtime_error("play option is missing script arg");
|
|
playArgs->script = convert_w2s(args[pos]);
|
|
LOG_ENTRY2("BootstrapperClient::ProcessArg = %d, value = %s", pos, playArgs->script.c_str());
|
|
if (pos+1 < count)
|
|
{
|
|
playArgs->authUrl = convert_w2s(args[++pos]);
|
|
LOG_ENTRY2("BootstrapperClient::ProcessArg paramIndex = %d, value = %s", pos, playArgs->authUrl.c_str());
|
|
}
|
|
|
|
if (pos+1 < count)
|
|
{
|
|
playArgs->authTicket = convert_w2s(args[++pos]);
|
|
LOG_ENTRY2("Bootstrapper::parseCmdLine paramIndex = %d, value = %s", pos, playArgs->authTicket.c_str());
|
|
}
|
|
|
|
if (pos+1 < count)
|
|
{
|
|
playArgs->guidName = convert_w2s(args[++pos]);
|
|
if (playArgs->guidName == "nonSilent")
|
|
{
|
|
playArgs->guidName = "";
|
|
}
|
|
LOG_ENTRY2("Bootstrapper::parseCmdLine paramIndex = %d, value = %s", pos, playArgs->guidName.c_str());
|
|
}
|
|
|
|
if (pos+1 < count)
|
|
{
|
|
playArgs->hiddenStartEventName = convert_w2s(args[++pos]);
|
|
LOG_ENTRY1("Hidden start event name = %s", playArgs->hiddenStartEventName.c_str());
|
|
if (playArgs->hiddenStartEventName == "nonHidden")
|
|
{
|
|
playArgs->hiddenStartEventName = "";
|
|
}
|
|
LOG_ENTRY2("Bootstrapper::parseCmdLine paramIndex = %d, value = %s", pos, playArgs->hiddenStartEventName.c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void BootstrapperClient::StartRobloxApp(bool fromInstall)
|
|
{
|
|
if (isDontStartApp())
|
|
{
|
|
LOG_ENTRY("We are in bg update, don't need to start game.");
|
|
return;
|
|
}
|
|
|
|
// disable Cancel button if we already got this far
|
|
Dialog()->ShowCancelButton(CMainDialog::CancelHide);
|
|
|
|
CMutex mutex(NULL, FALSE, StartRobloxAppMutex);
|
|
|
|
CTimedMutexLock lock(mutex);
|
|
while (lock.Lock(1) == WAIT_TIMEOUT )
|
|
{
|
|
LOG_ENTRY("Another process is starting watrbx. Abandoning startRobloxApp");
|
|
return;
|
|
}
|
|
|
|
bool waitForApp = true;
|
|
|
|
setStage(10);
|
|
|
|
message("Starting watrbx...");
|
|
|
|
LOG_ENTRY("Creating event");
|
|
CEvent robloxStartedEvent(NULL, TRUE, FALSE, _T("www.watrbx.wtf/robloxStartedEvent"));
|
|
LOG_ENTRY("Resetting event");
|
|
robloxStartedEvent.Reset();
|
|
|
|
CEvent gameReady;
|
|
if (playArgs && !playArgs->guidName.empty())
|
|
{
|
|
LOG_ENTRY1("Play game, syncGameEventName = %s", playArgs->guidName.c_str());
|
|
gameReady.Open(EVENT_MODIFY_STATE, FALSE, convert_s2w(playArgs->guidName).c_str());
|
|
}
|
|
|
|
::AllowSetForegroundWindow(ASFW_ANY);
|
|
|
|
CProcessInformation pi;
|
|
|
|
if (!playArgs)
|
|
{
|
|
bool isIDE = robloxAppArgs.find(std::wstring(_T("-ide"))) != std::wstring::npos;
|
|
if (!isIDE)
|
|
{
|
|
std::wstring url;
|
|
if (fromInstall)
|
|
url = format_string(_T("%S%s"), BaseHost().c_str(), _T("/download/thankyou"));
|
|
else
|
|
url = format_string(_T("%S%s"), BaseHost().c_str(), _T("/games"));
|
|
|
|
|
|
bool launcherStarted = false;
|
|
CEvent launcherStartedEvent;
|
|
LOG_ENTRY("Checking for launcher started...");
|
|
if (launcherStartedEvent.Open(EVENT_MODIFY_STATE, FALSE, LAUNCHER_STARTED_EVENT_NAME))
|
|
{
|
|
LOG_ENTRY("Launcher started.");
|
|
launcherStarted = true;
|
|
}
|
|
|
|
if (settings.GetValueShowInstallSuccessPrompt() && !launcherStarted && fromInstall)
|
|
{
|
|
LOG_ENTRY("Showing install success prompt.");
|
|
Dialog()->ShowSuccessPrompt();
|
|
}
|
|
else
|
|
{
|
|
if (url.find(_T("www.")) != 0 && url.find(_T("http:")) != 0)
|
|
{
|
|
url = format_string(_T("https://%s"), url.c_str());
|
|
}
|
|
LOG_ENTRY1("Redirectings to page url=%S", url.c_str());
|
|
ShellExecute(0, _T("open"), url.c_str(), 0, 0, 1);
|
|
}
|
|
|
|
waitForApp = false;
|
|
}
|
|
else
|
|
{
|
|
if (robloxAppArgs != std::wstring(_T("-ide")))
|
|
{
|
|
LOG_ENTRY("Starting windows player (beta)");
|
|
CreateProcess((programDirectory() + std::wstring(RobloxAppFileName)).c_str(), robloxAppArgs.c_str(), pi);
|
|
|
|
}
|
|
else //this is very rare corner case, we need to launch studio bootstrapper in this case
|
|
{
|
|
LOG_ENTRY("Starting studio bootstrapper from client bootstrapper");
|
|
std::string fileSubpath = std::string(STUDIOBOOTSTAPPERNAMEBETA);
|
|
std::wstring robloxPath = baseProgramDirectory(isPerUser());
|
|
robloxPath += convert_s2w(fileSubpath);
|
|
LOG_ENTRY1("Checking file existance %S", robloxPath.c_str());
|
|
if (FileSystem::IsFileExists(robloxPath.c_str()))
|
|
{
|
|
CreateProcess(robloxPath.c_str(), robloxAppArgs.c_str(), pi);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Dialog()->ShowWindow(CMainDialog::WindowSomethingInterestingShow);
|
|
|
|
std::string gameMode = "play";
|
|
LOG_ENTRY("Starting in play configuration");
|
|
|
|
std::wstring startArgs = format_string(_T("--%S -a %S -t %S -j %S"), gameMode.c_str(), playArgs->authUrl.c_str(), playArgs->authTicket.c_str(), playArgs->script.c_str());
|
|
if (!playArgs->hiddenStartEventName.empty())
|
|
{
|
|
startArgs.append(format_string(_T(" -w %S"), playArgs->hiddenStartEventName.c_str()));
|
|
}
|
|
if (!playArgs->browserTrackerId.empty())
|
|
{
|
|
startArgs.append(format_string(_T(" -b %S"), playArgs->browserTrackerId.c_str()));
|
|
}
|
|
LOG_ENTRY1("playArgs = %S", startArgs.c_str());
|
|
|
|
LOG_ENTRY("Starting windows player (beta)");
|
|
CreateProcess((programDirectory() + std::wstring(RobloxAppFileName)).c_str(), startArgs.c_str(), pi);
|
|
}
|
|
|
|
// TODO: Allow cancel of start process???
|
|
Dialog()->ShowCancelButton(CMainDialog::CancelHide);
|
|
|
|
// wait for Roblox to start
|
|
// This event is set in MainFrm.cpp
|
|
if (waitForApp)
|
|
{
|
|
LOG_ENTRY("startRobloxApp waiting for robloxStartedEvent");
|
|
influxDb.addPoint("WaitingForAppStart", GetElapsedTime() / 1000.0f);
|
|
::WaitForSingleObject(robloxStartedEvent, 10*1000);
|
|
influxDb.addPoint("WaitingForAppEnd", GetElapsedTime() / 1000.0f);
|
|
}
|
|
|
|
// find hwnd of launched process
|
|
launchedAppHwnd = GetHwndFromPID(pi.pi.dwProcessId);
|
|
|
|
if (playArgs && !playArgs->guidName.empty())
|
|
{
|
|
LOG_ENTRY("Setting game ready event");
|
|
gameReady.Set();
|
|
}
|
|
|
|
Dialog()->ShowCancelButton(CMainDialog::CancelShow);
|
|
//start Bootstrapper in HERE to do predeploy if
|
|
if (NeedPreDeployRun())
|
|
{
|
|
std::wstring args = _T("-qbgnorun");
|
|
CProcessInformation pi;
|
|
TCHAR pathBuffer[MAX_PATH];
|
|
::GetModuleFileName(0, pathBuffer, MAX_PATH);
|
|
|
|
LOG_ENTRY1("Starting up pre deploy bootstrapper, path = %s", pathBuffer);
|
|
CreateProcess(pathBuffer, args.c_str(), pi);
|
|
}
|
|
}
|
|
|
|
// installs bootstrapper for any version of studio
|
|
void BootstrapperClient::deployExtraStudioBootstrapper(std::string exeName, TCHAR *linkName, std::wstring componentId, bool forceDesktopIconCreation, const TCHAR *registryPath)
|
|
{
|
|
LOG_ENTRY3("BootstrapperClient::deployExtraStudioBootstrapper name = %s, linkName = %S, component = %S", exeName.c_str(), linkName, componentId.c_str());
|
|
bool success = true;
|
|
RegisterEvent(_T("BootstrapperDeployStudioBootstrapperStarted"));
|
|
try
|
|
{
|
|
LOG_ENTRY3("BootstrapperClient::deployExtraStudioBootstrapper name = %s, linkName = %S, component = %S", exeName.c_str(), linkName, componentId.c_str());
|
|
std::string fileSubpath = exeName;
|
|
std::wstring exePath = baseProgramDirectory(isPerUser());
|
|
exePath += convert_s2w(fileSubpath);
|
|
|
|
TCHAR version[MAX_PATH] = {0};
|
|
TCHAR url[MAX_PATH] = {0};
|
|
// look in registry for current version
|
|
getCurrentVersion(logger, isPerUser(), componentId.c_str(), version, sizeof(version)/sizeof(version[0]), url, sizeof(url)/sizeof(url[0]));
|
|
LOG_ENTRY2("BootstrapperClient::deployExtraStudioBootstrapper - Studio version = %S, url = %S", version, url);
|
|
|
|
try
|
|
{
|
|
// delete 2013 shortcut if any
|
|
deleteDesktopShortcut(logger, _T(STUDIOQTLINKNAME2013));
|
|
deleteProgramsShortcut(logger, isPerUser(), _T(STUDIOQTLINKNAME2013));
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
LOG_ENTRY1("BootstrapperClient::deployExtraStudioBootstrapper - failed to delete shortcut: %s", e.what());
|
|
}
|
|
|
|
bool studioOutOfDate = false;
|
|
|
|
if (settings.GetValueCheckIsStudioOutofDate())
|
|
{
|
|
CVersionInfo vi;
|
|
if (vi.Load(exePath))
|
|
{
|
|
std::string v = vi.GetFileVersionAsDotString();
|
|
if (v != settings.GetValueExeVersion())
|
|
studioOutOfDate = true;
|
|
}
|
|
}
|
|
|
|
if (!std::wstring(version).empty() && !studioOutOfDate && std::wstring(url) == convert_s2w(BaseHost()))
|
|
{
|
|
LOG_ENTRY("BootstrapperClient::deployExtraStudioBootstrapper - Studio bootstrapper version is present and URL is current one");
|
|
if (FileSystem::IsFileExists(exePath.c_str()))
|
|
{
|
|
LOG_ENTRY("BootstrapperClient::deployExtraStudioBootstrapper - Studio bootstrapper is present. updating links and leaving");
|
|
createRobloxShortcut(logger, isPerUser(), linkName, exePath.c_str(), _T("-ide"), false, true);
|
|
createRobloxShortcut(logger, isPerUser(), linkName, exePath.c_str(), _T("-ide"), true, forceDesktopIconCreation);
|
|
return;
|
|
}
|
|
}
|
|
|
|
std::wstring studioBootstrapper = simple_logger<wchar_t>::get_temp_filename(_T("tmp"));
|
|
{
|
|
std::ofstream bootstrapperFile(studioBootstrapper.c_str(), std::ios::binary);
|
|
// This version of the downloader doesn't show progress
|
|
// Fetch the most recent version of the exe
|
|
std::string versionedExeName = format_string("/%s-%s", fetchVersionGuid(VERSIONGUIDNAMESTUDIO).c_str(), exeName.c_str());
|
|
HttpTools::httpGetCdn(this, InstallHost(), versionedExeName.c_str(), std::string(), bootstrapperFile, false, boost::bind(&Bootstrapper::dummyProgress, _1, _2));
|
|
}
|
|
|
|
if (FileSystem::IsFileExists(exePath.c_str()))
|
|
{
|
|
LOG_ENTRY("BootstrapperClient::deployExtraStudioBootstrapper - deleting old studio bootstrapper");
|
|
DeleteFile(exePath.c_str());
|
|
}
|
|
|
|
copyFile(logger, studioBootstrapper.c_str(), exePath.c_str());
|
|
setCurrentVersion(logger, isPerUser(), componentId.c_str(), _T(""), _T(""));
|
|
|
|
createRobloxShortcut(logger, isPerUser(), linkName, exePath.c_str(), _T("-ide"), false, true);
|
|
createRobloxShortcut(logger, isPerUser(), linkName, exePath.c_str(), _T("-ide"), true, forceDesktopIconCreation);
|
|
|
|
// create registry info, so we can launch properly from web
|
|
std::wstring progName = convert_s2w(exeName);
|
|
CRegKey installKey = CreateKey(isPerUser() ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, registryPath, (baseProgramDirectory(isPerUser()) + progName).c_str());
|
|
throwHRESULT(installKey.SetStringValue(_T("install host"), convert_s2w(InstallHost()).c_str()), "Failed to set install host key value");
|
|
// update registry to point to stub studio bootstrapper
|
|
HKEY curKey = HKEY_LOCAL_MACHINE;
|
|
if (isPerUser())
|
|
{
|
|
curKey = HKEY_CURRENT_USER;
|
|
}
|
|
CRegKey key;
|
|
LONG result = key.Open(curKey, registryPath, KEY_WRITE);
|
|
if (ERROR_SUCCESS == result)
|
|
{
|
|
LOG_ENTRY("BootstrapperClient::deployExtraStudioBootstrapper - studio reg key opened");
|
|
result = key.SetStringValue(_T(""), exePath.c_str());
|
|
if (SUCCEEDED(result))
|
|
{
|
|
LOG_ENTRY1("BootstrapperClient::deployExtraStudioBootstrapper - studio reg key set to %S", exePath.c_str());
|
|
}
|
|
else
|
|
{
|
|
LOG_ENTRY1("BootstrapperClient::deployExtraStudioBootstrapper - studio reg key write failed, error=0x%X", result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_ENTRY1("BootstrapperClient::deployExtraStudioBootstrapper - studio reg key open failed, error=0x%X", result);
|
|
}
|
|
|
|
// create protocol handler registry
|
|
RegisterProtocolHandler(getQTStudioProtocolScheme(BaseHost()), exePath, registryPath);
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
success = false;
|
|
LOG_ENTRY1("BootstrapperClient::deployStudioBootstrapper - failed exception=%s", e.what());
|
|
}
|
|
|
|
if (success)
|
|
{
|
|
RegisterEvent(_T("BootstrapperDeployStudioBootstrapperSuccess"));
|
|
}
|
|
else
|
|
{
|
|
RegisterEvent(_T("BootstrapperDeployStudioBootstrapperFailure"));
|
|
}
|
|
}
|
|
|
|
void BootstrapperClient::deployStudioBetaBootstrapper(bool forceDesktopIconCreation)
|
|
{
|
|
deployExtraStudioBootstrapper(
|
|
std::string(STUDIOBOOTSTAPPERNAMEBETA),
|
|
_T(STUDIOQTLINKNAME_CUR),
|
|
getQTStudioCode(),
|
|
forceDesktopIconCreation,
|
|
getQTStudioRegistryPath().c_str() );
|
|
}
|
|
|
|
void BootstrapperClient::deployRobloxProxy(bool commitData)
|
|
{
|
|
deployer->deployVersionedFile(_T("RobloxProxy.zip"), NULL, Progress(), commitData);
|
|
CheckCancel();
|
|
|
|
if (!commitData)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!isPerUser())
|
|
{
|
|
//Global install in process, so remove local settings to prevent conflicts
|
|
CRegKey ckey;
|
|
if (!FAILED(ckey.Open(HKEY_CURRENT_USER, _T("Software\\Classes"))))
|
|
proxyModule.unregisterModule(ckey, true, logger);
|
|
}
|
|
|
|
proxyModule.registerModule(classesKey, programDirectory(), logger);
|
|
|
|
// create the 64 bit keys
|
|
CRegKey classesKey64;
|
|
throwHRESULT(classesKey64.Create(isPerUser() ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, _T("Software\\Classes"), REG_NONE, REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE | KEY_WOW64_64KEY), "Failed to create 64 bits HK**\\Software\\Classes");
|
|
LOG_ENTRY1("classesKey64 = %s\\Software\\Classes", (isPerUser() ? "HKEY_CURRENT_USER" : "HKEY_LOCAL_MACHINE"));
|
|
proxyModule64.registerModule(classesKey64, programDirectory(), logger);
|
|
|
|
{
|
|
// IE hack to avoid DLL security warning
|
|
// Ideally we'd just register the DLL as pre-approved, but this doesn't work in HKCU, only HKLM
|
|
CRegKey key;
|
|
throwHRESULT(key.Create(HKEY_CURRENT_USER, format_string(_T("Software\\Microsoft\\Windows\\CurrentVersion\\Ext\\Stats\\%s\\iexplore"), CLSID_Launcher).c_str()), "Failed to create key for iexplorer stats");
|
|
key.SetDWORDValue(_T("Flags"), 4);
|
|
|
|
// For IE8:
|
|
CRegKey allowedDomainsKey = CreateKey(key, _T("AllowedDomains"));
|
|
CreateKey(allowedDomainsKey, _T("watrbx.wtf"));
|
|
CreateKey(allowedDomainsKey, _T("pizzaboxer.fun"));
|
|
}
|
|
|
|
CVersionInfo vi;
|
|
std::wstring filePath = programDirectory() + _T("\\RobloxProxy.dll");
|
|
if (vi.Load(filePath))
|
|
{
|
|
std::string version = vi.GetFileVersionAsString();
|
|
CRegKey installKey;
|
|
if (!FAILED(installKey.Open(isPerUser() ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, GetRegistryPath().c_str())))
|
|
installKey.SetStringValue(_T("Plug-in version"), convert_s2w(version).c_str());
|
|
}
|
|
}
|
|
|
|
void BootstrapperClient::deployNPRobloxProxy(bool commitData)
|
|
{
|
|
deployer->deployVersionedFile(_T("NPRobloxProxy.zip"), NULL, Progress(), commitData);
|
|
CheckCancel();
|
|
|
|
if (!commitData)
|
|
{
|
|
return;
|
|
}
|
|
|
|
unregisterFirefoxPlugin(_T(FIREFOXREGKEY), false);
|
|
unregisterFirefoxPlugin(_T(FIREFOXREGKEY64), true);
|
|
|
|
registerFirefoxPlugin(_T(FIREFOXREGKEY), false);
|
|
registerFirefoxPlugin(_T(FIREFOXREGKEY64), true);
|
|
}
|
|
|
|
void BootstrapperClient::registerFirefoxPlugin(const TCHAR* id, bool is64Bits)
|
|
{
|
|
HKEY parent = HKEY_CURRENT_USER;
|
|
CRegKey key = CreateKey(parent, format_string(_T("SOFTWARE\\MozillaPlugins\\%s"), id).c_str(), NULL, is64Bits);
|
|
|
|
key.SetStringValue(_T("ProductName"), _T("Launcher"));
|
|
key.SetStringValue(_T("Description"), _T("watrbx launcher"));
|
|
key.SetStringValue(_T("Vendor"), _T("watrbx"));
|
|
key.SetStringValue(_T("Version"), _T("1"));
|
|
|
|
if (is64Bits)
|
|
key.SetStringValue(_T("Path"), (programDirectory() + _T("\\NPRobloxProxy64.dll")).c_str());
|
|
else
|
|
key.SetStringValue(_T("Path"), (programDirectory() + _T("\\NPRobloxProxy.dll")).c_str());
|
|
|
|
CreateKey(parent, format_string(_T("SOFTWARE\\MozillaPlugins\\%s\\MimeTypes"), id).c_str(), NULL, is64Bits);
|
|
CreateKey(parent, format_string(_T("SOFTWARE\\MozillaPlugins\\%s\\MimeTypes\\application/x-vnd-roblox-launcher"), id).c_str(), NULL, is64Bits);
|
|
}
|
|
|
|
void BootstrapperClient::unregisterFirefoxPlugin(const TCHAR* id, bool is64Bits)
|
|
{
|
|
CRegKey mozillaKey;
|
|
REGSAM sam = KEY_READ | KEY_WRITE;
|
|
if (is64Bits)
|
|
sam |= KEY_WOW64_64KEY;
|
|
|
|
if(!FAILED(mozillaKey.Open(HKEY_CURRENT_USER,_T("Software\\MozillaPlugins"), sam))){
|
|
DeleteKey(logger, mozillaKey, id);
|
|
}
|
|
}
|
|
|
|
void BootstrapperClient::DeployComponents(bool isUpdating, bool commitData)
|
|
{
|
|
std::vector<std::pair<std::wstring, std::wstring> > files;
|
|
createDirectory((programDirectory() + _T("content")).c_str());
|
|
createDirectory((programDirectory() + _T("content\\fonts")).c_str());
|
|
createDirectory((programDirectory() + _T("content\\music")).c_str());
|
|
createDirectory((programDirectory() + _T("content\\particles")).c_str());
|
|
createDirectory((programDirectory() + _T("content\\sky")).c_str());
|
|
createDirectory((programDirectory() + _T("content\\sounds")).c_str());
|
|
createDirectory((programDirectory() + _T("content\\textures")).c_str());
|
|
createDirectory((programDirectory() + _T("PlatformContent")).c_str());
|
|
createDirectory((programDirectory() + _T("PlatformContent\\pc")).c_str());
|
|
createDirectory((programDirectory() + _T("PlatformContent\\pc\\textures")).c_str());
|
|
createDirectory((programDirectory() + _T("PlatformContent\\pc\\terrain")).c_str());
|
|
createDirectory((programDirectory() + _T("shaders")).c_str());
|
|
|
|
//files.push_back(std::pair<std::wstring, std::wstring>(_T("redist.zip"), _T("")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("RobloxApp.zip"), _T("")));
|
|
//files.push_back(std::pair<std::wstring, std::wstring>(_T("Libraries.zip"), _T("")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("content-fonts.zip"), _T("content\\fonts\\")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("content-music.zip"), _T("content\\music\\")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("content-particles.zip"), _T("content\\particles\\")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("content-sky.zip"), _T("content\\sky\\")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("content-sounds.zip"), _T("content\\sounds\\")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("content-textures.zip"), _T("content\\textures\\")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("content-textures2.zip"), _T("content\\textures\\")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("content-textures3.zip"), _T("PlatformContent\\pc\\textures\\")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("content-terrain.zip"), _T("PlatformContent\\pc\\terrain\\")));
|
|
files.push_back(std::pair<std::wstring, std::wstring>(_T("shaders.zip"), _T("shaders\\")));
|
|
|
|
DoDeployComponents(files, isUpdating, commitData);
|
|
}
|
|
|
|
void BootstrapperClient::DoInstallApp()
|
|
{
|
|
LOG_ENTRY("BootstrapperClient::DoInstallApp");
|
|
const bool isUpdating = !queryInstalledVersion().empty();
|
|
|
|
validateAndFixChromeState();
|
|
|
|
deploySelf(true);
|
|
CheckCancel();
|
|
|
|
setStage(2);
|
|
|
|
// setup studio bootstrappers so we can launch studio (has to be done before we do much else, to make sure we launch right application)
|
|
installStudioLauncher(isUpdating);
|
|
|
|
// "install key" and "install host" must be set before RobloxProxy is deployed, because RobloxProxy uses these values
|
|
LOG_ENTRY("set install key");
|
|
CRegKey installKey = CreateKey(isPerUser() ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, GetRegistryPath().c_str(), (programDirectory() + BootstrapperFileName).c_str());
|
|
LOG_ENTRY("set install host key value");
|
|
throwHRESULT(installKey.SetStringValue(_T("install host"), convert_s2w(InstallHost()).c_str()), "Failed to set install host key value");
|
|
CheckCancel();
|
|
|
|
// Now deploy RobloxProxy so that javascript clients can get going ASAP (has to go after studio bootstrappers in case we are in editMode)
|
|
LOG_ENTRY("deployRobloxProxy()");
|
|
deployRobloxProxy(true);
|
|
CheckCancel();
|
|
|
|
LOG_ENTRY("deployNPRobloxProxy()");
|
|
deployNPRobloxProxy(true);
|
|
CheckCancel();
|
|
|
|
setStage(3);
|
|
|
|
// Now do the time-consuming download of all needed components
|
|
LOG_ENTRY("BootstrapperClient::DoInstallApp - DeployComponents()");
|
|
DeployComponents(isUpdating, true);
|
|
CheckCancel();
|
|
|
|
LOG_ENTRY("writeAppSettings()");
|
|
writeAppSettings();
|
|
CheckCancel();
|
|
|
|
setStage(4);
|
|
|
|
std::wstring dir = programDirectory();
|
|
RegisterElevationPolicy(RobloxAppFileName, dir.c_str());
|
|
CheckCancel();
|
|
|
|
LOG_ENTRY("BootstrapperClient::DoInstallApp - RegisterProtocolHandler()");
|
|
RegisterProtocolHandler(GetProtocolHandlerUrlScheme(), programDirectory() + BootstrapperFileName, GetRegistryPath());
|
|
|
|
//%%%%%%%%%%%%%%%%% POINT OF NO RETURN %%%%%%%%%%%%%%%%%%%%%
|
|
// No cancels allowed after this point
|
|
// TODO: Uninstall on failure???
|
|
setStage(5);
|
|
LOG_ENTRY("registerUninstall()");
|
|
RegisterUninstall(_T(PLAYERLINKNAME_CUR));
|
|
|
|
setStage(6);
|
|
try
|
|
{
|
|
LOG_ENTRY("createShortcuts()");
|
|
CreateShortcuts(!isUpdating, _T(PLAYERLINKNAME_CUR), BootstrapperFileName, _T("-browser"));
|
|
}
|
|
catch(std::exception& ex)
|
|
{
|
|
LOG_ENTRY1("Error: %s", exceptionToString(ex).c_str());
|
|
}
|
|
|
|
setStage(7);
|
|
|
|
// The very last thing to do is to set the version number
|
|
LOG_ENTRY("set version key value");
|
|
throwHRESULT(installKey.SetStringValue(_T("version"), convert_s2w(InstallVersion()).c_str()), "Failed to set version key value");
|
|
setCurrentVersion(logger, isPerUser(), getPlayerCode().c_str(), convert_s2w(InstallVersion()).c_str(), convert_s2w(BaseHost()).c_str());
|
|
setStage(8);
|
|
|
|
HRESULT hr = SheduleRobloxUpdater();
|
|
LOG_ENTRY1("SheduleRobloxUpdater hr = 0x%8.8x", hr);
|
|
}
|
|
|
|
void BootstrapperClient::installStudioLauncher(bool isUpdating)
|
|
{
|
|
try
|
|
{
|
|
deployStudioBetaBootstrapper(!isUpdating || hasLegacyStudioDesktopShortcut());
|
|
}
|
|
catch (std::exception& e)
|
|
{
|
|
LOG_ENTRY1("Error while deployStudioBetaBootstrapper: %s", exceptionToString(e).c_str());
|
|
}
|
|
}
|
|
|
|
void BootstrapperClient::DoUninstallApp(CRegKey &hk)
|
|
{
|
|
LOG_ENTRY("BootstrapperClient::DoUninstallApp");
|
|
|
|
{
|
|
CRegKey ckey;
|
|
if (FAILED(ckey.Open(hk, _T("Software\\Classes"))))
|
|
{
|
|
LOG_ENTRY("WARNING: Failed to open HK**\\Software\\Classes");
|
|
}
|
|
else
|
|
{
|
|
//unregisterFileTypes(ckey);
|
|
proxyModule.unregisterModule(ckey, isPerUser(), logger);
|
|
|
|
unregisterFirefoxPlugin(_T(FIREFOXREGKEY), false);
|
|
unregisterFirefoxPlugin(_T(FIREFOXREGKEY64), true);
|
|
}
|
|
|
|
// unregister 64 bit proxy module
|
|
CRegKey classesKey64;
|
|
if (!FAILED(classesKey64.Open(isPerUser() ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, _T("Software\\Classes"), KEY_READ | KEY_WRITE | KEY_WOW64_64KEY)))
|
|
{
|
|
proxyModule64.unregisterModule(classesKey64, isPerUser(), logger);
|
|
}
|
|
}
|
|
|
|
LOG_ENTRY("BootstrapperClient::DoUninstallApp - UnregisterProtocolHandler");
|
|
UnregisterProtocolHandler(GetProtocolHandlerUrlScheme());
|
|
|
|
LOG_ENTRY("Deleting roblox auto updater");
|
|
UninstallRobloxUpdater();
|
|
|
|
LOG_ENTRY("BootstrapperClient::DoUninstallApp - deleteCurVersionKeys");
|
|
deleteCurVersionKeys(logger, isPerUser(), getPlayerCode().c_str());
|
|
|
|
LOG_ENTRY("BootstrapperClient::DoUninstallApp - deleteDesktopShortcut");
|
|
deleteDesktopShortcut(logger, _T(PLAYERLINKNAME_CUR));
|
|
|
|
LOG_ENTRY("BootstrapperClient::DoUninstallApp - deleteProgramsShortcut");
|
|
deleteProgramsShortcut(logger, isPerUser(), _T(PLAYERLINKNAME_CUR));
|
|
|
|
// now check our studio bootstrappers, see if this needs to be removed as well (only if never installed!)
|
|
// essentially we check the registry key to see if studio was ever installed, if not we remove the registry key
|
|
// todo: turn this back on when we can do this in separated release! (US13812)
|
|
//checkStudioForUninstall(hk);
|
|
|
|
LOG_ENTRY("BootstrapperClient::DoUninstallApp - deleting program keys");
|
|
CRegKey key;
|
|
key.Open(hk, _T("Software"),KEY_WRITE);
|
|
DeleteKey(logger, key, GetRegistrySubPath().c_str());
|
|
}
|
|
|
|
void BootstrapperClient::checkStudioForUninstall(CRegKey &hk)
|
|
{
|
|
// cleanKey( getStudioRegistrySubPath(), hk);
|
|
// cleanKey( getQTStudioRegistrySubPath(), hk );
|
|
}
|
|
|
|
void BootstrapperClient::RegisterEvent(const TCHAR *eventName)
|
|
{
|
|
LOG_ENTRY1("BootstrapperClient::RegisterEvent event=%S", eventName);
|
|
counters->registerEvent(eventName, settings.GetValueCountersFireImmediately());
|
|
}
|
|
|
|
void BootstrapperClient::initialize()
|
|
{
|
|
LOG_ENTRY("BootstrapperClient::initialize");
|
|
counters.reset(new CountersClient(BaseHost(), "76E5A40C-3AE1-4028-9F10-7C62520BD94F", &logger));
|
|
|
|
{
|
|
proxyModule.appName = "RobloxProxy";
|
|
proxyModule.fileName = _T("RobloxProxy.DLL");
|
|
proxyModule.clsid = CLSID_Launcher;
|
|
proxyModule.typeLib = "{731B317A-E2B8-4BF7-A2C4-B47C225DDAFF}";
|
|
proxyModule.typeLibVersion = "1.0";
|
|
proxyModule.appID = AppID_Launcher;
|
|
proxyModule.interfaces.push_back(ComModule::Interface("ILauncher", "{699F0898-B7BC-4DE5-AFEE-0EC38AD42240}", false));
|
|
proxyModule.interfaces.push_back(ComModule::Interface("_ILauncherEvents", "{6E9600BE-5654-47F0-9A68-D6DC25FADC55}", true));
|
|
proxyModule.progID = "RobloxProxy.Launcher.4";
|
|
proxyModule.versionIndependentProgID = "RobloxProxy.Launcher";
|
|
proxyModule.name = _T("Launcher Class");
|
|
proxyModule.moduleName = LauncherFileName;
|
|
proxyModule.isDll = true;
|
|
proxyModule.is64Bits = false;
|
|
|
|
proxyModule64 = proxyModule;
|
|
proxyModule64.clsid = CLSID_Launcher64;
|
|
proxyModule64.moduleName = LauncherFileName64;
|
|
proxyModule64.fileName = _T("RobloxProxy64.DLL");
|
|
proxyModule64.is64Bits = true;
|
|
}
|
|
|
|
Bootstrapper::initialize();
|
|
}
|
|
|
|
void BootstrapperClient::createDialog()
|
|
{
|
|
dialog.reset(new CClientProgressDialog(hInstance, this));
|
|
dialog->InitDialog();
|
|
}
|
|
|
|
void BootstrapperClient::FlushEvents()
|
|
{
|
|
int load = settings.GetValueCountersLoad();
|
|
if (load > 100) // this should not be the case ever
|
|
{
|
|
load = 100;
|
|
}
|
|
|
|
if (load > 0)
|
|
{
|
|
if ((rand() % 100/load) == 0)
|
|
{
|
|
counters->reportEvents();
|
|
}
|
|
}
|
|
}
|
|
|
|
bool BootstrapperClient::PerModeLoggingEnabled() {
|
|
return settings.GetValuePerModeLoggingEnabled();
|
|
}
|
|
|
|
bool BootstrapperClient::UseNewCdn() {
|
|
return settings.GetValueUseNewCdn();
|
|
}
|
|
|