diff --git a/AppApi.cs b/AppApi.cs index 25351c27..0f5a8e2d 100644 --- a/AppApi.cs +++ b/AppApi.cs @@ -23,6 +23,8 @@ using System.Text; using System.Collections.Generic; using System.Threading; using System.IO.Pipes; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; namespace VRCX { @@ -77,6 +79,30 @@ namespace VRCX File.WriteAllText(configFile, json); } + public string GetVRChatAppDataLocation() + { + var json = ReadConfigFile(); + if (!string.IsNullOrEmpty(json)) + { + var obj = JsonConvert.DeserializeObject(json); + if (obj["cache_directory"] != null) + { + var cacheDir = (string)obj["cache_directory"]; + if (!string.IsNullOrEmpty(cacheDir) && Directory.Exists(cacheDir)) + { + return cacheDir; + } + } + } + var cachePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"Low\VRChat\VRChat"; + return cachePath; + } + + public string GetVRChatCacheLocation() + { + return Path.Combine(GetVRChatAppDataLocation(), "Cache-WindowsPlayer"); + } + public void ShowDevTools() { MainForm.Instance.Browser.ShowDevTools(); @@ -439,6 +465,53 @@ namespace VRCX return clipboard; } + public string GetVRChatRegistryKey(string key) + { + // https://answers.unity.com/questions/177945/playerprefs-changing-the-name-of-keys.html?childToView=208076#answer-208076 + // VRC_GROUP_ORDER_usr_032383a7-748c-4fb2-94e4-bcb928e5de6b_h2810492971 + uint hash = 5381; + foreach (char c in key) + hash = hash * 33 ^ c; + var keyName = key + "_h" + hash; + + using (var regKey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\VRChat\VRChat")) + { + var bytes = (byte[])regKey?.GetValue(keyName); + if (bytes == null) + return null; + + var value = Encoding.ASCII.GetString(bytes); + return value; + } + } + + public Dictionary GetVRChatModerations(string currentUserId) + { + var filePath = Path.Combine(GetVRChatAppDataLocation(), "LocalPlayerModerations", $"{currentUserId}-show-hide-user.vrcset"); + if (!File.Exists(filePath)) + return null; + + var output = new Dictionary(); + using (var reader = new StreamReader(filePath)) + { + string line; + int index; + string userId; + short type; + while ((line = reader.ReadLine()) != null) + { + index = line.IndexOf(' '); + if (index <= 0) + continue; + + userId = line.Substring(0, index); + type = short.Parse(line.Substring(line.Length - 3)); + output.Add(userId, type); + } + } + return output; + } + public void SetStartup(bool enabled) { try diff --git a/AssetBundleCacher.cs b/AssetBundleCacher.cs index 86c2cf1d..e92377ca 100644 --- a/AssetBundleCacher.cs +++ b/AssetBundleCacher.cs @@ -54,27 +54,24 @@ namespace VRCX return versionHex.PadLeft(32, '0'); } - public string GetVRChatCacheLocation(string cacheDir) + public string GetVRChatCacheLocation() { - var cachePath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + @"Low\VRChat\VRChat\Cache-WindowsPlayer"; - if (!string.IsNullOrEmpty(cacheDir) && Directory.Exists(cacheDir)) - cachePath = Path.Combine(cacheDir, @"Cache-WindowsPlayer"); - return cachePath; + return AppApi.Instance.GetVRChatCacheLocation(); } - public string GetVRChatCacheFullLocation(string id, int version, string cacheDir) + public string GetVRChatCacheFullLocation(string id, int version) { - var cachePath = GetVRChatCacheLocation(cacheDir); + var cachePath = GetVRChatCacheLocation(); var idHash = GetAssetId(id); var versionLocation = GetAssetVersion(version); return Path.Combine(cachePath, idHash, versionLocation); } - public long[] CheckVRChatCache(string id, int version, string cacheDir) + public long[] CheckVRChatCache(string id, int version) { long FileSize = -1; long IsLocked = 0; - var FullLocation = GetVRChatCacheFullLocation(id, version, cacheDir); + var FullLocation = GetVRChatCacheFullLocation(id, version); var FileLocation = Path.Combine(FullLocation, "__data"); if (File.Exists(FileLocation)) { @@ -116,9 +113,8 @@ namespace VRCX if (File.Exists(DownloadTempLocation)) File.Delete(DownloadTempLocation); } - catch(Exception) + catch (Exception) { - } DownloadProgress = -4; } @@ -159,16 +155,16 @@ namespace VRCX DownloadProgress = -16; } - public void DeleteCache(string cacheDir, string id, int version) + public void DeleteCache(string id, int version) { - var FullLocation = GetVRChatCacheFullLocation(id, version, cacheDir); + var FullLocation = GetVRChatCacheFullLocation(id, version); if (Directory.Exists(FullLocation)) Directory.Delete(FullLocation, true); } - public void DeleteAllCache(string cacheDir) + public void DeleteAllCache() { - var cachePath = GetVRChatCacheLocation(cacheDir); + var cachePath = GetVRChatCacheLocation(); if (Directory.Exists(cachePath)) { Directory.Delete(cachePath, true); @@ -176,9 +172,9 @@ namespace VRCX } } - public void SweepCache(string cacheDir) + public void SweepCache() { - var cachePath = GetVRChatCacheLocation(cacheDir); + var cachePath = GetVRChatCacheLocation(); if (!Directory.Exists(cachePath)) return; var directories = new DirectoryInfo(cachePath); @@ -205,9 +201,9 @@ namespace VRCX } } - public long GetCacheSize(string cacheDir) + public long GetCacheSize() { - var cachePath = GetVRChatCacheLocation(cacheDir); + var cachePath = GetVRChatCacheLocation(); if (Directory.Exists(cachePath)) { return DirSize(new DirectoryInfo(cachePath)); @@ -234,4 +230,4 @@ namespace VRCX return size; } } -} +} \ No newline at end of file diff --git a/html/src/app.js b/html/src/app.js index 77b7e885..cdfe6b83 100644 --- a/html/src/app.js +++ b/html/src/app.js @@ -2222,6 +2222,22 @@ speechSynthesis.getVoices(); } }); + API.$on('INSTANCE', function (args) { + var {json} = args; + if (!json) { + return; + } + var D = $app.groupDialog; + if ($app.groupDialog.visible && $app.groupDialog.id === json.ownerId) { + for (var instance of D.instances) { + if (instance.id === json.instanceId) { + instance.occupants = json.n_users; + break; + } + } + } + }); + // API: Friend API.$on('FRIEND:LIST', function (args) { @@ -2749,7 +2765,8 @@ speechSynthesis.getVoices(); for (var i = array.length - 1; i >= 0; i--) { if ( array[i].type === 'friendRequest' || - array[i].type === 'hiddenFriendRequest' + array[i].type === 'hiddenFriendRequest' || + array[i].type.includes('.') ) { array.splice(i, 1); } @@ -2894,7 +2911,6 @@ speechSynthesis.getVoices(); for (var json of args.json) { this.$emit('NOTIFICATION:V2', {json}); } - console.log('NOTIFICATION:V2:LIST', args); }); API.$on('NOTIFICATION:V2', function (args) { @@ -4586,7 +4602,7 @@ speechSynthesis.getVoices(); API, nextCurrentUserRefresh: 0, nextFriendsRefresh: 0, - nextAppUpdateCheck: 0, + nextAppUpdateCheck: 7200, ipcTimeout: 0, nextClearVRCXCacheCheck: 0, nextDiscordUpdate: 0, @@ -4609,6 +4625,9 @@ speechSynthesis.getVoices(); this.appVersion = version; this.comapreAppVersion(); this.setBranch(); + if (this.autoUpdateVRCX !== 'Off') { + this.checkForVRCXUpdate(); + } }); API.$on('SHOW_WORLD_DIALOG', (tag) => this.showWorldDialog(tag)); API.$on('SHOW_WORLD_DIALOG_SHORTNAME', (tag) => @@ -4942,6 +4961,7 @@ speechSynthesis.getVoices(); ); this.applyUserDialogLocation(); this.applyWorldDialogInstances(); + this.applyGroupDialogInstances(); feeds.pendingUpdate = false; }; @@ -8208,6 +8228,7 @@ speechSynthesis.getVoices(); $app.feedTableLookup(); // eslint-disable-next-line require-atomic-updates $app.notificationTable.data = await database.getNotifications(); + await this.refreshNotifications(); if (configRepository.getBool(`friendLogInit_${args.json.id}`)) { await $app.getFriendLog(); } else { @@ -8302,6 +8323,7 @@ speechSynthesis.getVoices(); this.getCurrentInstanceUserList(); this.applyUserDialogLocation(); this.applyWorldDialogInstances(); + this.applyGroupDialogInstances(); } }; @@ -8650,6 +8672,7 @@ speechSynthesis.getVoices(); this.lastVideoUrl = ''; this.applyUserDialogLocation(); this.applyWorldDialogInstances(); + this.applyGroupDialogInstances(); }; $app.data.lastLocation$ = { @@ -8910,6 +8933,7 @@ speechSynthesis.getVoices(); this.updateCurrentInstanceWorld(); this.applyUserDialogLocation(); this.applyWorldDialogInstances(); + this.applyGroupDialogInstances(); } break; case 'location': @@ -8928,6 +8952,7 @@ speechSynthesis.getVoices(); this.updateCurrentInstanceWorld(); this.applyUserDialogLocation(); this.applyWorldDialogInstances(); + this.applyGroupDialogInstances(); } var L = API.parseLocation(gameLog.location); var entry = { @@ -12243,9 +12268,11 @@ speechSynthesis.getVoices(); ) { database.addNotificationToDatabase(ref); } - $app.notifyMenu('notification'); - $app.unseenNotifications.push(ref.id); - $app.queueNotificationNoty(ref); + if ($app.friendLogInitStatus) { + $app.notifyMenu('notification'); + $app.unseenNotifications.push(ref.id); + $app.queueNotificationNoty(ref); + } } $app.notificationTable.data.push(ref); $app.updateSharedFeed(true); @@ -15674,15 +15701,24 @@ speechSynthesis.getVoices(); return; } var instances = {}; - for (var instance of inputInstances) { - instances[instance.location] = { - id: instance.instanceId, - tag: instance.location, - occupants: instance.memberCount, + for (var instance of D.instances) { + instances[instance.tag] = { + ...instance, friendCount: 0, users: [] }; } + if (typeof inputInstances !== 'undefined') { + for (var instance of inputInstances) { + instances[instance.location] = { + id: instance.instanceId, + tag: instance.location, + occupants: instance.memberCount, + friendCount: 0, + users: [] + }; + } + } var currentLocation = this.lastLocation.location; if (this.lastLocation.location === 'traveling') { currentLocation = this.lastLocationDestination; @@ -19545,15 +19581,6 @@ speechSynthesis.getVoices(); this.WriteVRChatConfigFile(); }; - $app.methods.getVRChatCacheDir = async function () { - await this.readVRChatConfigFile(); - var cacheDirectory = ''; - if (this.VRChatConfigFile.cache_directory) { - cacheDirectory = this.VRChatConfigFile.cache_directory; - } - return cacheDirectory; - }; - $app.data.VRChatResolutions = [ {name: '1280x720 (720p)', width: 1280, height: 720}, {name: '1920x1080 (1080p Default)', width: '', height: ''}, @@ -19702,11 +19729,10 @@ speechSynthesis.getVoices(); } }; - $app.methods.checkVRChatCache = async function (ref) { + $app.methods.checkVRChatCache = function (ref) { if (!ref.unityPackages) { return [-1, 0]; } - var cacheDir = await this.getVRChatCacheDir(); var assetUrl = ''; for (var i = ref.unityPackages.length - 1; i > -1; i--) { var unityPackage = ref.unityPackages[i]; @@ -19723,7 +19749,7 @@ speechSynthesis.getVoices(); if (!id || !version) { return [-1, 0]; } - return AssetBundleCacher.CheckVRChatCache(id, version, cacheDir); + return AssetBundleCacher.CheckVRChatCache(id, version); }; API.getBundles = function (fileId) { @@ -19858,7 +19884,6 @@ speechSynthesis.getVoices(); }; $app.methods.deleteVRChatCache = async function (ref) { - var cacheDir = await this.getVRChatCacheDir(); var assetUrl = ''; for (var i = ref.unityPackages.length - 1; i > -1; i--) { var unityPackage = ref.unityPackages[i]; @@ -19872,7 +19897,7 @@ speechSynthesis.getVoices(); } var id = extractFileId(assetUrl); var version = parseInt(extractFileVersion(assetUrl), 10); - await AssetBundleCacher.DeleteCache(cacheDir, id, version); + await AssetBundleCacher.DeleteCache(id, version); this.getVRChatCacheSize(); this.updateVRChatWorldCache(); this.updateVRChatAvatarCache(); @@ -19892,8 +19917,7 @@ speechSynthesis.getVoices(); }; $app.methods.deleteAllVRChatCache = async function () { - var cacheDir = await this.getVRChatCacheDir(); - await AssetBundleCacher.DeleteAllCache(cacheDir); + await AssetBundleCacher.DeleteAllCache(); this.getVRChatCacheSize(); }; @@ -19904,8 +19928,7 @@ speechSynthesis.getVoices(); }; $app.methods.sweepVRChatCache = async function () { - var cacheDir = await this.getVRChatCacheDir(); - await AssetBundleCacher.SweepCache(cacheDir); + await AssetBundleCacher.SweepCache(); if (this.VRChatConfigDialog.visible) { this.getVRChatCacheSize(); } @@ -19917,13 +19940,12 @@ speechSynthesis.getVoices(); $app.methods.getVRChatCacheSize = async function () { this.VRChatCacheSizeLoading = true; - var cacheDir = await this.getVRChatCacheDir(); var totalCacheSize = 20; if (this.VRChatConfigFile.cache_size) { totalCacheSize = this.VRChatConfigFile.cache_size; } this.VRChatTotalCacheSize = totalCacheSize; - var usedCacheSize = await AssetBundleCacher.GetCacheSize(cacheDir); + var usedCacheSize = await AssetBundleCacher.GetCacheSize(); this.VRChatUsedCacheSize = (usedCacheSize / 1073741824).toFixed(2); this.VRChatCacheSizeLoading = false; }; @@ -19975,16 +19997,13 @@ speechSynthesis.getVoices(); } var fileId = extractFileId(assetUrl); var fileVersion = parseInt(extractFileVersion(assetUrl), 10); - var cacheDir = await this.getVRChatCacheDir(); var assetLocation = await AssetBundleCacher.GetVRChatCacheFullLocation( fileId, - fileVersion, - cacheDir + fileVersion ); var cacheInfo = await AssetBundleCacher.CheckVRChatCache( fileId, - fileVersion, - cacheDir + fileVersion ); var inCache = false; if (cacheInfo[0] > 0) { @@ -20142,6 +20161,33 @@ speechSynthesis.getVoices(); } } this.userDialog.isGroupsLoading = false; + if (userId === API.currentUser.id) { + this.sortCurrentUserGroups(); + } + }; + + $app.methods.sortCurrentUserGroups = function () { + var groupList = []; + var sortGroups = function (a, b) { + var aIndex = groupList.indexOf(a.id); + var bIndex = groupList.indexOf(b.id); + if (aIndex === -1 && bIndex === -1) { + return 0; + } + if (aIndex === -1) { + return 1; + } + if (bIndex === -1) { + return -1; + } + return aIndex - bIndex; + }; + AppApi.GetVRChatRegistryKey( + `VRC_GROUP_ORDER_${API.currentUser.id}` + ).then((json) => { + groupList = JSON.parse(json); + this.userGroups.remainingGroups.sort(sortGroups); + }); }; // gallery @@ -20585,7 +20631,9 @@ speechSynthesis.getVoices(); } this.notifyMenu('settings'); var type = 'Auto'; - if (this.autoUpdateVRCX === 'Notify') { + if (!API.isLoggedIn) { + this.showVRCXUpdateDialog(); + } else if (this.autoUpdateVRCX === 'Notify') { this.showVRCXUpdateDialog(); } else if (this.autoUpdateVRCX === 'Auto Download') { var autoInstall = false; @@ -23210,6 +23258,30 @@ speechSynthesis.getVoices(); } }); + /* + params: { + groupId: string + } + */ + + API.getGroupRoles = function (params) { + return this.call(`groups/${params.groupId}/roles`, { + method: 'GET', + params + }).then((json) => { + var args = { + json, + params + }; + this.$emit('GROUP:ROLES', args); + return args; + }); + }; + + API.$on('GROUP:ROLES', function (args) { + console.log(args); + }); + /* params: { groupId: string @@ -23342,7 +23414,9 @@ speechSynthesis.getVoices(); ref: {}, announcement: {}, members: [], - instances: [] + instances: [], + roles: [], + memberRoles: [] }; $app.methods.showGroupDialog = function (groupId) { @@ -23360,6 +23434,8 @@ speechSynthesis.getVoices(); D.treeData = []; D.announcement = {}; D.instances = []; + D.roles = []; + D.memberRoles = []; if (this.groupDialogLastMembers !== groupId) { D.members = []; } @@ -23436,6 +23512,23 @@ speechSynthesis.getVoices(); this.applyGroupDialogInstances(args3.json); } }); + API.getGroupRoles({ + groupId + }).then((args4) => { + if (groupId === args4.params.groupId) { + D.roles = args4.json; + for (var role of args4.json) { + if ( + D.ref && + D.ref.myMember && + Array.isArray(D.ref.myMember.roleIds) && + D.ref.myMember.roleIds.includes(role.id) + ) { + D.memberRoles.push(role); + } + } + } + }); } if (this.$refs.groupDialogTabs.currentName === '0') { this.groupDialogLastActiveTab = 'Info'; @@ -23505,7 +23598,13 @@ speechSynthesis.getVoices(); $app.methods.refreshGroupDialogTreeData = function () { var D = this.groupDialog; - D.treeData = buildTreeData(D.ref); + D.treeData = buildTreeData({ + group: D.ref, + announcement: D.announcement, + roles: D.roles, + instances: D.instances, + members: D.members + }); }; $app.methods.joinGroup = function (groupId) { diff --git a/html/src/index.pug b/html/src/index.pug index 590bdad8..81af3aa7 100644 --- a/html/src/index.pug +++ b/html/src/index.pug @@ -871,7 +871,7 @@ html el-tooltip(placement="top" content="Refresh" :disabled="hideTooltips") el-button(type="default" @click="API.getConfig()" size="mini" icon="el-icon-refresh" circle style="margin-left:5px") .x-friend-list(style="margin-top:10px") - .x-friend-item(v-for="(link, item) in API.cachedConfig.downloadUrls" :key="item" v-if="item !== 'sdk2'" placement="top") + .x-friend-item(v-for="(link, item) in API.cachedConfig.downloadUrls" :key="item" placement="top") .detail(@click="openExternalLink(link)") span.name(v-text="item") span.extra(v-text="link") @@ -2334,6 +2334,25 @@ html .detail span.name Joined At span.extra {{ groupDialog.ref.myMember.joinedAt | formatDate('long') }} + .x-friend-item(style="cursor:default") + .detail + span.name Roles + span.extra(v-if="groupDialog.memberRoles.length === 0") - + span.extra(v-else) + template(v-for="(role, rIndex) in groupDialog.memberRoles" :key="rIndex") + el-tooltip(placement="top") + template(#content) + span Description: {{ role.description }} + br + span(v-if="role.updatedAt") Updated At: {{ role.updatedAt | formatDate('long') }} + span(v-else) Created At: {{ role.createdAt | formatDate('long') }} + br + span Permissions: + br + template(v-for="(permission, pIndex) in role.permissions" :key="pIndex") + span {{ permission }} + br + span {{ role.name }}{{ rIndex < groupDialog.memberRoles.length - 1 ? ', ' : '' }} el-tab-pane(label="Members") template(v-if="groupDialog.visible && groupDialog.ref.membershipStatus === 'member'") span(v-if="hasGroupPermission(groupDialog.ref, 'group-members-viewall')" style="font-weight:bold;font-size:16px") All Members