VRCX self updater

This commit is contained in:
Natsumi
2021-07-28 23:11:29 +12:00
parent 5604976864
commit 4937febe8e
7 changed files with 325 additions and 40 deletions
+24
View File
@@ -297,6 +297,30 @@ namespace VRCX
broadcastSocket.SendTo(byteBuffer, endPoint);
}
public void DownloadVRCXUpdate(string url, string AppVersion)
{
var Location = Path.Combine(Program.BaseDirectory, "update.zip");
WebClient client = new WebClient();
client.Headers.Add("user-agent", AppVersion);
client.DownloadFile(new System.Uri(url), Location);
}
public void RestartApplication()
{
System.Diagnostics.Process VRCXProcess = new System.Diagnostics.Process();
VRCXProcess.StartInfo.FileName = Path.Combine(Program.BaseDirectory, "VRCX.exe");
VRCXProcess.StartInfo.UseShellExecute = false;
VRCXProcess.Start();
System.Environment.Exit(0);
}
public bool checkForUpdateZip()
{
if (File.Exists(Path.Combine(Program.BaseDirectory, "update.zip")))
return true;
return false;
}
public void SetStartup(bool enabled)
{
try
+12 -2
View File
@@ -33,6 +33,7 @@ namespace VRCX
public static string DownloadTempLocation;
public static int DownloadProgress;
public static bool DownloadCanceled;
public static bool IsUpdate;
public static string AssetId;
public static string AssetVersion;
public static int AssetSize;
@@ -98,7 +99,7 @@ namespace VRCX
return -1;
}
public void DownloadCacheFile(string cacheDir, string url, string id, int version, int sizeInBytes, string md5, string AppVersion)
public void DownloadCacheFile(string cacheDir, string url, string id, int version, int sizeInBytes, string md5, string AppVersion, bool IsUpdate)
{
if (!File.Exists(Path.Combine(Program.BaseDirectory, "AssetBundleCacher\\AssetBundleCacher.exe")))
{
@@ -160,6 +161,11 @@ namespace VRCX
Directory.CreateDirectory(AssetBundleCacherTemp);
AssetBundleCacherArgs = $@" -url ""file:\\{DownloadTempLocation}"" -id ""{id}"" -ver {version} -batchmode -path ""{AssetBundleCacherTemp}""";
DownloadCanceled = false;
if (IsUpdate)
{
AssetBundleCacher.IsUpdate = true;
DownloadTempLocation = Path.Combine(Program.BaseDirectory, "update.zip");
}
client = new WebClient();
client.Headers.Add("user-agent", AppVersion);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressCallback);
@@ -216,6 +222,11 @@ namespace VRCX
DownloadProgress = -15;
return;
}
if (IsUpdate)
{
DownloadProgress = -16;
return;
}
FileInfo data = new FileInfo(DownloadTempLocation);
if (data.Length != AssetSize)
{
@@ -242,7 +253,6 @@ namespace VRCX
DownloadProgress = -13;
return;
}
if (DownloadCanceled)
{
if (File.Exists(DownloadTempLocation))
+3 -1
View File
@@ -1,4 +1,4 @@
// Copyright(c) 2019 pypy. All rights reserved.
// Copyright(c) 2019 pypy. All rights reserved.
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
@@ -33,6 +33,8 @@ namespace VRCX
private static void Run()
{
Update.Check();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
+79
View File
@@ -0,0 +1,79 @@
// Copyright(c) 2019-2021 pypy. All rights reserved.
//
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.
using System;
using System.IO;
using System.IO.Compression;
using System.Windows.Forms;
namespace VRCX
{
class Update
{
public static void Check()
{
try
{
var CurrentDirectory = new DirectoryInfo(Program.BaseDirectory);
FileInfo[] Files = CurrentDirectory.GetFiles();
foreach (FileInfo FileDetails in Files)
{
var FilePath = Path.Combine(Program.BaseDirectory, FileDetails.Name);
if (FileDetails.Extension == ".old")
File.Delete(FilePath);
}
var Location = Path.Combine(Program.BaseDirectory, "update.zip");
if (File.Exists(Location))
Install();
}
catch (Exception e)
{
MessageBox.Show(e.ToString(), "Update failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
public static void Install()
{
var Location = Path.Combine(Program.BaseDirectory, "update.zip");
if (!File.Exists(Location))
return;
if (File.Exists(Path.Combine(Program.BaseDirectory, "VRCX.exe.old")))
File.Delete(Path.Combine(Program.BaseDirectory, "VRCX.exe.old"));
if (File.Exists(Path.Combine(Program.BaseDirectory, "VRCX.exe")))
File.Move(Path.Combine(Program.BaseDirectory, "VRCX.exe"), Path.Combine(Program.BaseDirectory, "VRCX.exe.old"));
var CurrentDirectory = new DirectoryInfo(Program.BaseDirectory);
FileInfo[] Files = CurrentDirectory.GetFiles();
foreach (FileInfo FileDetails in Files)
{
var FilePath = Path.Combine(Program.BaseDirectory, $"{FileDetails.Name}.old");
if (FileDetails.Extension == ".dll")
{
if (File.Exists(FilePath))
File.Delete(FilePath);
File.Move(Path.Combine(Program.BaseDirectory, FileDetails.Name), FilePath);
}
}
using (ZipArchive archive = ZipFile.OpenRead(Location))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
var path = Path.Combine(Program.BaseDirectory, entry.FullName);
if (entry.Name == "")
{
Directory.CreateDirectory(Path.GetDirectoryName(path));
continue;
}
entry.ExtractToFile(path, true);
}
}
File.Delete(Location);
System.Diagnostics.Process VRCXProcess = new System.Diagnostics.Process();
VRCXProcess.StartInfo.FileName = Path.Combine(Program.BaseDirectory, "VRCX.exe");
VRCXProcess.StartInfo.UseShellExecute = false;
VRCXProcess.Start();
System.Environment.Exit(0);
}
}
}
+3
View File
@@ -66,6 +66,8 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Management" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
@@ -79,6 +81,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="AssetBundleCacher.cs" />
<Compile Include="Update.cs" />
<Compile Include="CefService.cs" />
<Compile Include="Discord.cs" />
<Compile Include="CpuMonitor.cs" />
+171 -33
View File
@@ -3749,6 +3749,7 @@ speechSynthesis.getVoices();
API,
nextCurrentUserRefresh: 0,
nextFriendsRefresh: 0,
nextAppUpdateCheck: 0,
isGameRunning: false,
isGameNoVR: false,
appVersion,
@@ -3762,7 +3763,6 @@ speechSynthesis.getVoices();
watch: {},
el: '#x-app',
mounted() {
this.checkAppVersion();
API.$on('SHOW_WORLD_DIALOG', (tag) => this.showWorldDialog(tag));
API.$on('SHOW_LAUNCH_DIALOG', (tag) => this.showLaunchDialog(tag));
this.updateLoop();
@@ -3809,35 +3809,6 @@ speechSynthesis.getVoices();
return style;
};
$app.methods.checkAppVersion = async function () {
var response = await webApiService.execute({
url: 'https://api.github.com/repos/pypy-vrc/VRCX/releases/latest',
method: 'GET',
headers: {
'User-Agent': 'VRCX'
}
});
var json = JSON.parse(response.data);
if (json === Object(json) &&
json.name &&
json.published_at) {
this.latestAppVersion = `${json.name} (${formatDate(json.published_at, 'YYYY-MM-DD HH24:MI:SS')})`;
if (json.name > this.appVersion) {
new Noty({
type: 'info',
text: `Update available!!<br>${this.latestAppVersion}`,
timeout: 60000,
callbacks: {
onClick: () => AppApi.OpenLink('https://github.com/pypy-vrc/VRCX/releases')
}
}).show();
this.notifyMenu('settings');
}
} else {
this.latestAppVersion = 'Error occured';
}
};
$app.methods.updateLoop = function () {
try {
if (API.isLoggedIn === true) {
@@ -3854,6 +3825,12 @@ speechSynthesis.getVoices();
API.refreshPlayerModerations();
}
}
if (--this.nextAppUpdateCheck <= 0) {
this.nextAppUpdateCheck = 43200; // 6hours
if (this.autoUpdateVRCX !== 'Off') {
this.checkForVRCXUpdate();
}
}
AppApi.CheckGameRunning().then(([isGameRunning, isGameNoVR]) => {
if (isGameRunning !== this.isGameRunning) {
this.isGameRunning = isGameRunning;
@@ -7652,6 +7629,8 @@ speechSynthesis.getVoices();
$app.data.autoSweepVRChatCache = configRepository.getBool('VRCX_autoSweepVRChatCache');
$app.data.vrBackgroundEnabled = configRepository.getBool('VRCX_vrBackgroundEnabled');
$app.data.asideWidth = configRepository.getInt('VRCX_asidewidth');
$app.data.autoUpdateVRCX = configRepository.getString('VRCX_autoUpdateVRCX');
$app.data.branch = configRepository.getString('VRCX_branch');
var saveOpenVROption = function () {
configRepository.setBool('openVR', this.openVR);
configRepository.setBool('openVRAlways', this.openVRAlways);
@@ -7776,6 +7755,20 @@ speechSynthesis.getVoices();
$app.data.asideWidth = 236;
configRepository.setInt('VRCX_asidewidth', $app.data.asideWidth);
}
if (!configRepository.getString('VRCX_autoUpdateVRCX')) {
$app.data.autoUpdateVRCX = 'Notify';
configRepository.setString('VRCX_autoUpdateVRCX', $app.data.autoUpdateVRCX);
}
if (!configRepository.getString('VRCX_branch')) {
$app.data.branch = 'Stable';
if (appVersion.substring(0, 24) === 'VRCX.PyPyDance.Companion') {
$app.data.branch = 'Beta';
}
configRepository.setString('VRCX_branch', $app.data.branch);
}
if (!configRepository.getString('VRCX_lastVRCXVersion')) {
configRepository.setString('VRCX_lastVRCXVersion', appVersion);
}
if (!configRepository.getString('sharedFeedFilters')) {
var sharedFeedFilters = {
noty: {
@@ -12461,6 +12454,12 @@ speechSynthesis.getVoices();
var { ref, type } = this.downloadCurrent;
this.downloadQueue.delete(ref.id);
this.downloadQueueTable.data = Array.from(this.downloadQueue.values());
if (this.downloadCurrent.id === 'VRCXUpdate') {
var url = this.downloadCurrent.updateZipUrl;
await AssetBundleCacher.DownloadCacheFile('', url, '', 0, 0, '', appVersion, true);
this.downloadVRChatCacheProgress();
return;
}
var assetUrl = '';
for (var i = ref.unityPackages.length - 1; i > -1; i--) {
var unityPackage = ref.unityPackages[i];
@@ -12512,7 +12511,7 @@ speechSynthesis.getVoices();
}
var { url, md5, sizeInBytes } = file;
var cacheDir = await this.getVRChatCacheDir();
await AssetBundleCacher.DownloadCacheFile(cacheDir, url, ref.id, ref.version, sizeInBytes, md5, appVersion);
await AssetBundleCacher.DownloadCacheFile(cacheDir, url, ref.id, ref.version, sizeInBytes, md5, appVersion, false);
this.downloadVRChatCacheProgress();
};
@@ -12628,7 +12627,7 @@ speechSynthesis.getVoices();
if (this.worldDialog.id === this.downloadCurrent.id) {
this.updateVRChatCache();
}
if (this.downloadCurrent.type === 'manual') {
if (this.downloadCurrent.type === 'Manual') {
this.$message({
message: 'World cache complete',
type: 'success'
@@ -12681,7 +12680,7 @@ speechSynthesis.getVoices();
if (this.worldDialog.id === this.downloadCurrent.id) {
this.updateVRChatCache();
}
if (this.downloadCurrent.type === 'manual') {
if (this.downloadCurrent.type === 'Manual') {
this.$message({
message: 'File already in cache',
type: 'warning'
@@ -12734,6 +12733,21 @@ speechSynthesis.getVoices();
this.downloadInProgress = false;
this.downloadVRChatCache();
return;
case -16:
this.downloadCurrent.status = 'Success';
this.downloadCurrent.date = Date.now();
this.downloadHistoryTable.data.unshift(this.downloadCurrent);
if (this.downloadCurrent.autoInstall) {
this.restartVRCX();
} else {
this.downloadDialog.visible = false;
this.showVRCXUpdateDialog();
}
this.downloadCurrent = {};
this.downloadProgress = 0;
this.downloadInProgress = false;
this.downloadVRChatCache();
return;
default:
this.downloadProgress = downloadProgress;
}
@@ -13146,6 +13160,130 @@ speechSynthesis.getVoices();
configRepository.setInt('VRCX_asidewidth', this.asideWidth);
};
// VRCX auto update
$app.data.VRCXUpdateDialog = {
visible: false,
updatePending: false,
release: '',
releases: []
};
$app.data.checkingForVRCXUpdate = false;
$app.data.branches = {
Stable: { name: 'Stable', urlReleases: 'https://api.github.com/repos/pypy-vrc/VRCX/releases', urlLatest: 'https://api.github.com/repos/pypy-vrc/VRCX/releases/latest' },
Beta: { name: 'Beta', urlReleases: 'https://api.github.com/repos/natsumi-sama/VRCX/releases', urlLatest: 'https://api.github.com/repos/natsumi-sama/VRCX/releases/latest' }
};
$app.methods.showVRCXUpdateDialog = async function () {
this.$nextTick(() => adjustDialogZ(this.$refs.VRCXUpdateDialog.$el));
var D = this.VRCXUpdateDialog;
D.visible = true;
D.updatePending = await AppApi.checkForUpdateZip();
this.loadBranchVersions();
};
$app.methods.downloadVRCXUpdate = function (updateZipUrl, name, type, autoInstall) {
var ref = {
id: 'VRCXUpdate',
name
};
this.downloadQueue.set('VRCXUpdate', { ref, type, updateZipUrl, autoInstall });
this.downloadQueueTable.data = Array.from(this.downloadQueue.values());
if (!this.downloadInProgress) {
this.downloadVRChatCache();
}
};
$app.methods.installVRCXUpdate = function () {
for (var release of this.VRCXUpdateDialog.releases) {
if (release.name === this.VRCXUpdateDialog.release) {
var downloadUrl = release.assets[0].browser_download_url;
var name = release.name;
var type = 'Manual';
var autoInstall = false;
this.downloadVRCXUpdate(downloadUrl, name, type, autoInstall);
this.VRCXUpdateDialog.visible = false;
this.showDownloadDialog();
}
}
};
$app.methods.restartVRCX = function () {
AppApi.RestartApplication();
};
$app.methods.loadBranchVersions = async function () {
var D = this.VRCXUpdateDialog;
var url = this.branches[this.branch].urlReleases;
this.checkingForVRCXUpdate = true;
var response = await webApiService.execute({
url,
method: 'GET',
headers: {
'User-Agent': appVersion
}
});
this.checkingForVRCXUpdate = false;
var json = JSON.parse(response.data);
D.releases = json;
D.release = json[0].name;
if (configRepository.getString('VRCX_branch') !== this.branch) {
configRepository.setString('VRCX_branch', this.branch);
}
};
$app.methods.saveAutoUpdateVRCX = function () {
configRepository.setString('VRCX_autoUpdateVRCX', this.autoUpdateVRCX);
};
$app.methods.checkForVRCXUpdate = async function () {
if (await AppApi.checkForUpdateZip()) {
return;
}
var url = this.branches[this.branch].urlLatest;
this.checkingForVRCXUpdate = true;
var response = await webApiService.execute({
url,
method: 'GET',
headers: {
'User-Agent': appVersion
}
});
this.checkingForVRCXUpdate = false;
var json = JSON.parse(response.data);
if (json === Object(json) &&
json.name &&
json.published_at) {
this.latestAppVersion = `${json.name} (${formatDate(json.published_at, 'YYYY-MM-DD HH24:MI:SS')})`;
if (json.name > this.appVersion) {
if ((json.assets[0].content_type !== 'application/x-zip-compressed') || (json.assets[0].state !== 'uploaded')) {
return;
}
this.notifyMenu('settings');
var downloadUrl = json.assets[0].browser_download_url;
var name = json.name;
var type = 'Auto';
if (this.autoUpdateVRCX === 'Notify') {
this.showVRCXUpdateDialog();
} else if (this.autoUpdateVRCX === 'Auto Download') {
if (downloadUrl) {
var autoInstall = false;
this.downloadVRCXUpdate(downloadUrl, name, type, autoInstall);
}
} else if (this.autoUpdateVRCX === 'Auto Install') {
if (downloadUrl) {
var autoInstall = true;
this.downloadVRCXUpdate(downloadUrl, name, type, autoInstall);
}
}
}
} else {
this.latestAppVersion = 'Error occured';
}
};
$app = new Vue($app);
window.$app = $app;
}());
+33 -4
View File
@@ -652,7 +652,7 @@ html
.detail
span.name Version
span.extra(v-text="appVersion")
.x-friend-item(@click="checkAppVersion()")
.x-friend-item(style="cursor:default")
.detail
span.name Latest Version
span.extra(v-if="latestAppVersion" v-text="latestAppVersion")
@@ -661,6 +661,18 @@ html
.detail
span.name Repository URL
span.extra https://github.com/pypy-vrc/VRCX
div.options-container
span.sub-header VRCX Updater
div.options-container-item
el-button(size="small" icon="el-icon-upload" @click="showVRCXUpdateDialog()") Check for update
div.options-container-item
span.name Auto update:
br
el-radio-group(v-model="autoUpdateVRCX" @change="saveAutoUpdateVRCX" size="mini")
el-radio-button(label="Off")
el-radio-button(label="Notify")
el-radio-button(label="Auto Download")
el-radio-button(label="Auto Install")
div.options-container
span.header Appearance
div.options-container-item
@@ -1678,7 +1690,7 @@ html
template(v-if="downloadQueueTable.data.length >= 1")
span(style="margin-top:15px") Queue:
data-tables(v-bind="downloadQueueTable" style="margin-top:10px")
el-table-column(label="World Name" prop="name")
el-table-column(label="Name" prop="name")
template(v-once #default="scope")
span.x-link(v-text="scope.row.ref.name" @click="showWorldDialog(scope.row.location)")
el-table-column(label="User Name" prop="name" width="150")
@@ -1693,9 +1705,12 @@ html
el-table-column(label="Time" prop="date" width="90")
template(v-once #default="scope")
timer(:epoch="scope.row.date")
el-table-column(label="World Name" prop="name")
el-table-column(label="Name" prop="name")
template(v-once #default="scope")
span.x-link(v-text="scope.row.ref.name" @click="showWorldDialog(scope.row.location)")
template(v-if="scope.row.ref.id === 'VRCXUpdate'")
el-button(size="small" @click="showVRCXUpdateDialog") VRCX Update
template(v-else)
span.x-link(v-text="scope.row.ref.name" @click="showWorldDialog(scope.row.location)")
el-table-column(label="User Name" prop="name" width="150")
template(v-once #default="scope")
span.x-link(v-text="getDisplayName(scope.row.userId)" @click="showUserDialog(scope.row.userId)")
@@ -1704,6 +1719,20 @@ html
template(#footer)
el-button(v-if="downloadQueue.size >= 1" size="small" @click="cancelAllVRChatCacheDownload") Cancel All
el-button(size="small" @click="downloadDialog.visible = false") Close
//- dialog: update VRCX
el-dialog.x-dialog(ref="VRCXUpdateDialog" :visible.sync="VRCXUpdateDialog.visible" title="VRCX Updater" width="400px")
div(v-loading="checkingForVRCXUpdate" style="margin-top:15px")
template(v-if="VRCXUpdateDialog.updatePending")
span Update ready for install, restart VRCX to apply.
template(v-else)
el-select(v-model="branch" @change="loadBranchVersions" style="display:inline-block;width:150px;margin-right:15px")
el-option(v-once v-for="branch in branches" :key="branch.name" :label="branch.name" :value="branch.name")
el-select(v-model="VRCXUpdateDialog.release" style="display:inline-block;width:150px")
el-option(v-for="item in VRCXUpdateDialog.releases" :key="item.name" :label="item.tag_name" :value="item.name")
template(#footer)
el-button(v-if="!VRCXUpdateDialog.updatePending && VRCXUpdateDialog.release !== appVersion" type="primary" size="small" @click="installVRCXUpdate") Download
el-button(v-if="VRCXUpdateDialog.updatePending" type="primary" size="small" @click="restartVRCX") Install
//- dialog: launch
el-dialog.x-dialog(ref="launchDialog" :visible.sync="launchDialog.visible" title="Launch" width="400px")