Fix cache size and switch to using fileAnalysis for bundle sizes

This commit is contained in:
Natsumi
2025-08-10 22:54:31 +12:00
parent d40d0af21f
commit e182b4f2c2
9 changed files with 102 additions and 150 deletions

View File

@@ -28,16 +28,14 @@ namespace VRCX
public string GetAssetId(string id, string variant = "")
{
using (var sha256 = SHA256.Create())
using var sha256 = SHA256.Create();
var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(id + variant));
var idHex = new StringBuilder(hash.Length * 2);
foreach (var b in hash)
{
byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(id + variant));
StringBuilder idHex = new StringBuilder(hash.Length * 2);
foreach (byte b in hash)
{
idHex.AppendFormat("{0:x2}", b);
}
return idHex.ToString().ToUpper().Substring(0, 16);
idHex.Append($"{b:x2}");
}
return idHex.ToString().ToUpper().Substring(0, 16);
}
public string GetAssetVersion(int version, int variantVersion = 0)
@@ -136,7 +134,7 @@ namespace VRCX
if (File.Exists(fileLocation))
{
cachePath = fullLocation;
FileInfo data = new FileInfo(fileLocation);
var data = new FileInfo(fileLocation);
fileSize = data.Length;
}
if (File.Exists(Path.Join(fullLocation, "__lock")))
@@ -241,8 +239,8 @@ namespace VRCX
public long DirSize(DirectoryInfo d)
{
long size = 0;
FileInfo[] files = d.GetFiles("*.*", SearchOption.AllDirectories);
foreach (FileInfo file in files)
var files = d.GetFiles("*.*", SearchOption.AllDirectories);
foreach (var file in files)
{
size += file.Length;
}

View File

@@ -217,8 +217,7 @@ namespace VRCX
{
var fileHashBytes = await sha256.ComputeHashAsync(stream, _cancellationToken);
var fileHashString = Convert.ToHexString(fileHashBytes);
if (!string.IsNullOrEmpty(fileHashString) &&
!hashString.Equals(fileHashString, StringComparison.OrdinalIgnoreCase))
if (!hashString.Equals(fileHashString, StringComparison.OrdinalIgnoreCase))
{
logger.Error($"Hash check failed file:{fileHashString} web:{hashString}");
throw new Exception("Hash check failed");

View File

@@ -122,7 +122,7 @@ const avatarReq = {
/**
* @param {{ avatarId: string }} params
* @returns {Promise<{json: T, params}>}
* @returns {Promise<{json: any, params}>}
*/
deleteImposter(params) {
return request(`avatars/${params.avatarId}/impostor`, {
@@ -151,7 +151,7 @@ const avatarReq = {
},
/**
* @param {{ avatarId: string }} avatarId
* @param {string} avatarId
* @returns {Promise<{json: any, params}>}
*/
getAvatarGallery(avatarId) {
@@ -174,7 +174,7 @@ const avatarReq = {
},
/**
* @param {{ imageData: string, avatarId: string }} params
* @param {{ imageData: string, avatarId: string }}
* @returns {Promise<{json: any, params}>}
*/
uploadAvatarGalleryImage(imageData, avatarId) {

View File

@@ -553,18 +553,6 @@
icon="el-icon-refresh"
circle
@click="refreshAvatarDialogTreeData"></el-button>
<el-tooltip
placement="top"
:content="t('dialog.avatar.json.file_analysis')"
:disabled="hideTooltips">
<el-button
type="default"
size="mini"
icon="el-icon-s-data"
circle
style="margin-left: 5px"
@click="getAvatarFileAnalysis"></el-button>
</el-tooltip>
<el-button
type="default"
size="mini"
@@ -573,8 +561,8 @@
style="margin-left: 5px"
@click="downloadAndSaveJson(avatarDialog.id, avatarDialog.ref)"></el-button>
<el-tree
v-if="Object.keys(fileAnalysis).length > 0"
:data="fileAnalysis"
v-if="Object.keys(avatarDialog.fileAnalysis).length > 0"
:data="avatarDialog.fileAnalysis"
style="margin-top: 5px; font-size: 12px">
<template #default="scope">
<span>
@@ -665,7 +653,6 @@
const treeData = ref([]);
const timeSpent = ref(0);
const memo = ref('');
const fileAnalysis = ref({});
const setAvatarTagsDialog = reactive({
visible: false,
loading: false,
@@ -740,7 +727,7 @@
}
function handleDialogOpen() {
fileAnalysis.value = {};
avatarDialog.value.fileAnalysis = {};
memo.value = '';
treeData.value = [];
getAvatarTimeSpent();
@@ -1085,66 +1072,6 @@
treeData.value = buildTreeData(avatarDialog.value.ref);
}
function getAvatarFileAnalysis() {
let unityPackage;
const D = avatarDialog.value;
const avatarId = D.ref.id;
let assetUrl = '';
let variant = 'security';
for (let i = D.ref.unityPackages.length - 1; i > -1; i--) {
unityPackage = D.ref.unityPackages[i];
if (unityPackage.variant !== 'security') {
continue;
}
if (unityPackage.platform === 'standalonewindows' && compareUnityVersion(unityPackage.unitySortNumber)) {
assetUrl = unityPackage.assetUrl;
break;
}
}
if (!assetUrl) {
for (let i = D.ref.unityPackages.length - 1; i > -1; i--) {
unityPackage = D.ref.unityPackages[i];
if (unityPackage.variant !== 'standard') {
continue;
}
if (
unityPackage.platform === 'standalonewindows' &&
compareUnityVersion(unityPackage.unitySortNumber)
) {
variant = 'standard';
assetUrl = unityPackage.assetUrl;
break;
}
}
}
const fileId = extractFileId(assetUrl);
const version = parseInt(extractFileVersion(assetUrl), 10);
if (!fileId || !version) {
$message({
message: 'File Analysis unavailable',
type: 'error'
});
return;
}
miscRequest.getFileAnalysis({ fileId, version, variant }).then((args) => {
if (!avatarDialog.value.visible || avatarDialog.value.id !== avatarId) {
return;
}
const ref = args.json;
if (typeof ref.fileSize !== 'undefined') {
ref._fileSize = `${(ref.fileSize / 1048576).toFixed(2)} MB`;
}
if (typeof ref.uncompressedSize !== 'undefined') {
ref._uncompressedSize = `${(ref.uncompressedSize / 1048576).toFixed(2)} MB`;
}
if (typeof ref.avatarStats?.totalTextureUsage !== 'undefined') {
ref._totalTextureUsage = `${(ref.avatarStats.totalTextureUsage / 1048576).toFixed(2)} MB`;
}
fileAnalysis.value = buildTreeData(args.json);
});
}
function showSetAvatarTagsDialog(avatarId) {
const D = setAvatarTagsDialog;
D.visible = true;

View File

@@ -722,6 +722,17 @@
circle
style="margin-left: 5px"
@click="downloadAndSaveJson(worldDialog.id, worldDialog.ref)"></el-button>
<el-tree
v-if="Object.keys(worldDialog.fileAnalysis).length > 0"
:data="worldDialog.fileAnalysis"
style="margin-top: 5px; font-size: 12px">
<template #default="scope">
<span>
<span style="font-weight: bold; margin-right: 5px" v-text="scope.data.key"></span>
<span v-if="!scope.data.children" v-text="scope.data.value"></span>
</span>
</template>
</el-tree>
<el-tree :data="treeData" style="margin-top: 5px; font-size: 12px">
<template #default="{ data }">
<span>

View File

@@ -267,7 +267,7 @@ const gameLog = {
async getVisitCount(worldId) {
var ref = {
visitCount: '',
visitCount: 0,
worldId: ''
};
await sqliteService.execute(

View File

@@ -86,7 +86,9 @@ async function deleteVRChatCache(ref) {
compareUnityVersion(unityPackage.unitySortNumber)
) {
assetUrl = unityPackage.assetUrl;
if (unityPackage.variant !== 'standard') {
if (!unityPackage.variant || unityPackage.variant === 'standard') {
variant = 'security';
} else {
variant = unityPackage.variant;
}
break;
@@ -119,7 +121,9 @@ async function checkVRChatCache(ref) {
compareUnityVersion(unityPackage.unitySortNumber)
) {
assetUrl = unityPackage.assetUrl;
if (unityPackage.variant !== 'standard') {
if (!unityPackage.variant || unityPackage.variant === 'standard') {
variant = 'security';
} else {
variant = unityPackage.variant;
}
break;
@@ -424,6 +428,7 @@ async function getBundleDateSize(ref) {
const { currentInstanceWorld, currentInstanceLocation } =
storeToRefs(instanceStore);
const bundleSizes = [];
const bundleJson = [];
for (let i = ref.unityPackages.length - 1; i > -1; i--) {
const unityPackage = ref.unityPackages[i];
if (
@@ -443,59 +448,63 @@ async function getBundleDateSize(ref) {
}
const assetUrl = unityPackage.assetUrl;
const fileId = extractFileId(assetUrl);
const fileVersion = parseInt(extractFileVersion(assetUrl), 10);
if (!fileId) {
const version = parseInt(extractFileVersion(assetUrl), 10);
let variant = '';
if (!unityPackage.variant || unityPackage.variant === 'standard') {
variant = 'security';
} else {
variant = unityPackage.variant;
}
if (!fileId || !version) {
continue;
}
const args = await miscRequest.getBundles(fileId);
if (!args?.json?.versions) {
console.log(
`Fetching bundle size for ${platform} - fileId: ${fileId}, version: ${version}, variant: ${variant}`
);
const args = await miscRequest.getFileAnalysis({
fileId,
version,
variant
});
if (!args?.json?.success) {
continue;
}
let { versions } = args.json;
for (let j = versions.length - 1; j > -1; j--) {
const version = versions[j];
if (version.version === fileVersion) {
const createdAt = version.created_at;
const fileSize = `${(
version.file.sizeInBytes / 1048576
).toFixed(2)} MB`;
bundleSizes[platform] = {
createdAt,
fileSize
};
const json = args.json;
if (typeof json.fileSize !== 'undefined') {
json._fileSize = `${(json.fileSize / 1048576).toFixed(2)} MB`;
}
if (typeof json.uncompressedSize !== 'undefined') {
json._uncompressedSize = `${(json.uncompressedSize / 1048576).toFixed(2)} MB`;
}
if (typeof json.avatarStats?.totalTextureUsage !== 'undefined') {
json._totalTextureUsage = `${(json.avatarStats.totalTextureUsage / 1048576).toFixed(2)} MB`;
}
bundleJson[platform] = json;
const createdAt = json.created_at;
const fileSize = `${(json.fileSize / 1048576).toFixed(2)} MB`;
bundleSizes[platform] = {
createdAt,
fileSize
};
// update avatar dialog
if (avatarDialog.value.id === ref.id) {
avatarDialog.value.bundleSizes[platform] =
bundleSizes[platform];
if (avatarDialog.value.lastUpdated < version.created_at) {
avatarDialog.value.lastUpdated = version.created_at;
}
}
// update world dialog
if (worldDialog.value.id === ref.id) {
worldDialog.value.bundleSizes[platform] =
bundleSizes[platform];
if (worldDialog.value.lastUpdated < version.created_at) {
worldDialog.value.lastUpdated = version.created_at;
}
}
// update player list
if (currentInstanceLocation.value.worldId === ref.id) {
currentInstanceWorld.value.bundleSizes[platform] =
bundleSizes[platform];
if (
currentInstanceWorld.value.lastUpdated <
version.created_at
) {
currentInstanceWorld.value.lastUpdated =
version.created_at;
}
}
break;
}
// update avatar dialog
if (avatarDialog.value.id === ref.id) {
avatarDialog.value.bundleSizes[platform] = bundleSizes[platform];
avatarDialog.value.lastUpdated = createdAt;
avatarDialog.value.fileAnalysis = buildTreeData(bundleJson);
}
// update world dialog
if (worldDialog.value.id === ref.id) {
worldDialog.value.bundleSizes[platform] = bundleSizes[platform];
worldDialog.value.lastUpdated = createdAt;
worldDialog.value.fileAnalysis = buildTreeData(bundleJson);
}
// update player list
if (currentInstanceLocation.value.worldId === ref.id) {
currentInstanceWorld.value.bundleSizes[platform] =
bundleSizes[platform];
currentInstanceWorld.value.lastUpdated = createdAt;
}
}

View File

@@ -50,9 +50,10 @@ export const useAvatarStore = defineStore('Avatar', () => {
galleryLoading: false,
lastUpdated: '',
inCache: false,
cacheSize: 0,
cacheSize: '',
cacheLocked: false,
cachePath: ''
cachePath: '',
fileAnalysis: {}
},
cachedAvatarModerations: new Map(),
avatarHistory: new Set(),
@@ -131,9 +132,12 @@ export const useAvatarStore = defineStore('Avatar', () => {
highestPrice: null,
id: '',
imageUrl: '',
listingDate: null,
lock: false,
lowestPrice: null,
name: '',
pendingUpload: false,
performance: {},
productId: null,
publishedListings: [],
releaseStatus: '',
@@ -205,9 +209,10 @@ export const useAvatarStore = defineStore('Avatar', () => {
D.loading = true;
D.id = avatarId;
D.inCache = false;
D.cacheSize = 0;
D.cacheSize = '';
D.cacheLocked = false;
D.cachePath = '';
D.fileAnalysis = {};
D.isQuestFallback = false;
D.isPC = false;
D.isQuest = false;
@@ -350,7 +355,7 @@ export const useAvatarStore = defineStore('Avatar', () => {
const D = state.avatarDialog;
if (D.visible) {
D.inCache = false;
D.cacheSize = 0;
D.cacheSize = '';
D.cacheLocked = false;
D.cachePath = '';
checkVRChatCache(D.ref).then((cacheInfo) => {

View File

@@ -39,9 +39,10 @@ export const useWorldStore = defineStore('World', () => {
bundleSizes: [],
lastUpdated: '',
inCache: false,
cacheSize: 0,
cacheSize: '',
cacheLocked: false,
cachePath: '',
fileAnalysis: {},
lastVisit: '',
visitCount: 0,
timeSpent: 0,
@@ -96,11 +97,13 @@ export const useWorldStore = defineStore('World', () => {
D.visible = true;
D.loading = true;
D.inCache = false;
D.cacheSize = 0;
D.cacheSize = '';
D.cacheLocked = false;
D.cachePath = '';
D.fileAnalysis = {};
D.rooms = [];
D.lastVisit = '';
D.visitCount = '';
D.visitCount = 0;
D.timeSpent = 0;
D.isFavorite = false;
D.avatarScalingDisabled = false;
@@ -211,7 +214,7 @@ export const useWorldStore = defineStore('World', () => {
const D = state.worldDialog;
if (D.visible) {
D.inCache = false;
D.cacheSize = 0;
D.cacheSize = '';
D.cacheLocked = false;
D.cachePath = '';
checkVRChatCache(D.ref).then((cacheInfo) => {