mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-11 10:53:52 +02:00
1227 lines
42 KiB
JavaScript
1227 lines
42 KiB
JavaScript
import { defineStore } from 'pinia';
|
|
import Vue, { computed, reactive, watch } from 'vue';
|
|
import { instanceRequest, userRequest, worldRequest } from '../api';
|
|
import { $app } from '../app';
|
|
import configRepository from '../service/config';
|
|
import { database } from '../service/database';
|
|
import { watchState } from '../service/watchState';
|
|
import { instanceContentSettings } from '../shared/constants';
|
|
import {
|
|
checkVRChatCache,
|
|
compareByDisplayName,
|
|
compareByLocationAt,
|
|
displayLocation,
|
|
getAvailablePlatforms,
|
|
getBundleDateSize,
|
|
getGroupName,
|
|
getWorldName,
|
|
hasGroupPermission,
|
|
isRealInstance,
|
|
parseLocation
|
|
} from '../shared/utils';
|
|
import { useFriendStore } from './friend';
|
|
import { useGroupStore } from './group';
|
|
import { useLocationStore } from './location';
|
|
import { useNotificationStore } from './notification';
|
|
import { usePhotonStore } from './photon';
|
|
import { useAppearanceSettingsStore } from './settings/appearance';
|
|
import { useSharedFeedStore } from './sharedFeed';
|
|
import { useUiStore } from './ui';
|
|
import { useUserStore } from './user';
|
|
import { useWorldStore } from './world';
|
|
import { useI18n } from 'vue-i18n-bridge';
|
|
|
|
export const useInstanceStore = defineStore('Instance', () => {
|
|
const locationStore = useLocationStore();
|
|
const worldStore = useWorldStore();
|
|
const friendStore = useFriendStore();
|
|
const appearanceSettingsStore = useAppearanceSettingsStore();
|
|
const groupStore = useGroupStore();
|
|
const notificationStore = useNotificationStore();
|
|
const uiStore = useUiStore();
|
|
const userStore = useUserStore();
|
|
const sharedFeedStore = useSharedFeedStore();
|
|
const photonStore = usePhotonStore();
|
|
const { t } = useI18n();
|
|
|
|
const state = reactive({
|
|
cachedInstances: new Map(),
|
|
currentInstanceWorld: {
|
|
ref: {},
|
|
instance: {},
|
|
isPC: false,
|
|
isQuest: false,
|
|
isIos: false,
|
|
avatarScalingDisabled: false,
|
|
focusViewDisabled: false,
|
|
inCache: false,
|
|
cacheSize: '',
|
|
bundleSizes: [],
|
|
lastUpdated: ''
|
|
},
|
|
currentInstanceLocation: {},
|
|
queuedInstances: new Map(),
|
|
previousInstancesInfoDialogVisible: false,
|
|
previousInstancesInfoDialogInstanceId: '',
|
|
instanceJoinHistory: new Map(),
|
|
currentInstanceUserList: {
|
|
data: [],
|
|
tableProps: {
|
|
stripe: true,
|
|
size: 'mini',
|
|
defaultSort: {
|
|
prop: 'timer',
|
|
order: 'descending'
|
|
}
|
|
},
|
|
layout: 'table'
|
|
},
|
|
updatePlayerListTimer: null,
|
|
updatePlayerListPending: false
|
|
});
|
|
|
|
const cachedInstances = computed({
|
|
get() {
|
|
return state.cachedInstances;
|
|
},
|
|
set(value) {
|
|
state.cachedInstances = value;
|
|
}
|
|
});
|
|
|
|
const currentInstanceWorld = computed({
|
|
get: () => state.currentInstanceWorld,
|
|
set: (value) => {
|
|
state.currentInstanceWorld = value;
|
|
}
|
|
});
|
|
|
|
const currentInstanceLocation = computed({
|
|
get: () => state.currentInstanceLocation,
|
|
set: (value) => {
|
|
state.currentInstanceLocation = value;
|
|
}
|
|
});
|
|
|
|
const queuedInstances = computed({
|
|
get: () => state.queuedInstances,
|
|
set: (value) => {
|
|
state.queuedInstances = value;
|
|
}
|
|
});
|
|
|
|
const previousInstancesInfoDialogVisible = computed({
|
|
get: () => state.previousInstancesInfoDialogVisible,
|
|
set: (value) => {
|
|
state.previousInstancesInfoDialogVisible = value;
|
|
}
|
|
});
|
|
|
|
const previousInstancesInfoDialogInstanceId = computed({
|
|
get: () => state.previousInstancesInfoDialogInstanceId,
|
|
set: (value) => {
|
|
state.previousInstancesInfoDialogInstanceId = value;
|
|
}
|
|
});
|
|
|
|
const instanceJoinHistory = computed({
|
|
get: () => state.instanceJoinHistory,
|
|
set: (value) => {
|
|
state.instanceJoinHistory = value;
|
|
}
|
|
});
|
|
|
|
const currentInstanceUserList = computed({
|
|
get: () => state.currentInstanceUserList,
|
|
set: (value) => {
|
|
state.currentInstanceUserList = value;
|
|
}
|
|
});
|
|
|
|
watch(
|
|
() => watchState.isLoggedIn,
|
|
(isLoggedIn) => {
|
|
state.currentInstanceUserList.data = [];
|
|
state.instanceJoinHistory = new Map();
|
|
state.previousInstancesInfoDialogVisible = false;
|
|
state.cachedInstances.clear();
|
|
state.queuedInstances.clear();
|
|
if (isLoggedIn) {
|
|
getInstanceJoinHistory();
|
|
}
|
|
},
|
|
{ flush: 'sync' }
|
|
);
|
|
|
|
async function getInstanceJoinHistory() {
|
|
state.instanceJoinHistory = await database.getInstanceJoinHistory();
|
|
}
|
|
|
|
function addInstanceJoinHistory(location, dateTime) {
|
|
if (!location || !dateTime) {
|
|
return;
|
|
}
|
|
|
|
if (state.instanceJoinHistory.has(location)) {
|
|
state.instanceJoinHistory.delete(location);
|
|
}
|
|
|
|
const epoch = new Date(dateTime).getTime();
|
|
state.instanceJoinHistory.set(location, epoch);
|
|
}
|
|
|
|
function showPreviousInstancesInfoDialog(instanceId) {
|
|
state.previousInstancesInfoDialogVisible = true;
|
|
state.previousInstancesInfoDialogInstanceId = instanceId;
|
|
}
|
|
|
|
function updateCurrentInstanceWorld() {
|
|
let L;
|
|
let instanceId = locationStore.lastLocation.location;
|
|
if (locationStore.lastLocation.location === 'traveling') {
|
|
instanceId = locationStore.lastLocationDestination;
|
|
}
|
|
if (!instanceId) {
|
|
state.currentInstanceWorld = {
|
|
ref: {},
|
|
instance: {},
|
|
isPC: false,
|
|
isQuest: false,
|
|
isIos: false,
|
|
avatarScalingDisabled: false,
|
|
focusViewDisabled: false,
|
|
inCache: false,
|
|
cacheSize: '',
|
|
bundleSizes: [],
|
|
lastUpdated: ''
|
|
};
|
|
state.currentInstanceLocation = {};
|
|
} else if (instanceId !== state.currentInstanceLocation.tag) {
|
|
state.currentInstanceWorld = {
|
|
ref: {},
|
|
instance: {},
|
|
isPC: false,
|
|
isQuest: false,
|
|
isIos: false,
|
|
avatarScalingDisabled: false,
|
|
focusViewDisabled: false,
|
|
inCache: false,
|
|
cacheSize: '',
|
|
bundleSizes: [],
|
|
lastUpdated: ''
|
|
};
|
|
L = parseLocation(instanceId);
|
|
state.currentInstanceLocation = L;
|
|
worldRequest
|
|
.getWorld({
|
|
worldId: L.worldId
|
|
})
|
|
.then((args) => {
|
|
state.currentInstanceWorld.ref = args.ref;
|
|
const { isPC, isQuest, isIos } = getAvailablePlatforms(
|
|
args.ref.unityPackages
|
|
);
|
|
state.currentInstanceWorld.isPC = isPC;
|
|
state.currentInstanceWorld.isQuest = isQuest;
|
|
state.currentInstanceWorld.isIos = isIos;
|
|
state.currentInstanceWorld.avatarScalingDisabled =
|
|
args.ref?.tags.includes(
|
|
'feature_avatar_scaling_disabled'
|
|
);
|
|
state.currentInstanceWorld.focusViewDisabled =
|
|
args.ref?.tags.includes('feature_focus_view_disabled');
|
|
checkVRChatCache(args.ref)
|
|
.then((cacheInfo) => {
|
|
if (cacheInfo.Item1 > 0) {
|
|
state.currentInstanceWorld.inCache = true;
|
|
state.currentInstanceWorld.cacheSize = `${(
|
|
cacheInfo.Item1 / 1048576
|
|
).toFixed(2)} MB`;
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.error(
|
|
'Error checking VRChat cache:',
|
|
error
|
|
);
|
|
});
|
|
getBundleDateSize(args.ref)
|
|
.then((bundleSizes) => {
|
|
state.currentInstanceWorld.bundleSizes =
|
|
bundleSizes;
|
|
})
|
|
.catch((error) => {
|
|
console.error(
|
|
'Error fetching bundle sizes:',
|
|
error
|
|
);
|
|
});
|
|
return args;
|
|
})
|
|
.catch((error) => {
|
|
console.error('Error fetching world data:', error);
|
|
});
|
|
} else {
|
|
worldRequest
|
|
.getCachedWorld({
|
|
worldId: state.currentInstanceLocation.worldId
|
|
})
|
|
.then((args) => {
|
|
state.currentInstanceWorld.ref = args.ref;
|
|
const { isPC, isQuest, isIos } = getAvailablePlatforms(
|
|
args.ref.unityPackages
|
|
);
|
|
state.currentInstanceWorld.isPC = isPC;
|
|
state.currentInstanceWorld.isQuest = isQuest;
|
|
state.currentInstanceWorld.isIos = isIos;
|
|
checkVRChatCache(args.ref).then((cacheInfo) => {
|
|
if (cacheInfo.Item1 > 0) {
|
|
state.currentInstanceWorld.inCache = true;
|
|
state.currentInstanceWorld.cacheSize = `${(
|
|
cacheInfo.Item1 / 1048576
|
|
).toFixed(2)} MB`;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
if (isRealInstance(instanceId)) {
|
|
const ref = state.cachedInstances.get(instanceId);
|
|
if (typeof ref !== 'undefined') {
|
|
state.currentInstanceWorld.instance = ref;
|
|
} else {
|
|
L = parseLocation(instanceId);
|
|
if (L.isRealInstance) {
|
|
instanceRequest
|
|
.getInstance({
|
|
worldId: L.worldId,
|
|
instanceId: L.instanceId
|
|
})
|
|
.then((args) => {
|
|
state.currentInstanceWorld.instance = args.ref;
|
|
})
|
|
.catch((error) => {
|
|
console.error(
|
|
'Error fetching instance data:',
|
|
error
|
|
);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {object} json
|
|
* @returns {object} ref
|
|
*/
|
|
function applyInstance(json) {
|
|
if (!json?.id) {
|
|
return null;
|
|
}
|
|
if (!json.$fetchedAt) {
|
|
json.$fetchedAt = new Date().toJSON();
|
|
}
|
|
let ref = state.cachedInstances.get(json.id);
|
|
if (typeof ref === 'undefined') {
|
|
ref = {
|
|
id: '',
|
|
location: '',
|
|
instanceId: '',
|
|
name: '',
|
|
worldId: '',
|
|
type: '',
|
|
ownerId: '',
|
|
tags: [],
|
|
active: false,
|
|
full: false,
|
|
n_users: 0,
|
|
hasCapacityForYou: true, // not present depending on endpoint
|
|
capacity: 0,
|
|
recommendedCapacity: 0,
|
|
userCount: 0,
|
|
queueEnabled: false, // only present with group instance type
|
|
queueSize: 0, // only present when queuing is enabled
|
|
platforms: {},
|
|
gameServerVersion: 0,
|
|
hardClose: null, // boolean or null
|
|
closedAt: null, // string or null
|
|
secureName: '',
|
|
shortName: '',
|
|
world: {},
|
|
users: [], // only present when you're the owner
|
|
clientNumber: '',
|
|
contentSettings: {},
|
|
photonRegion: '',
|
|
region: '',
|
|
canRequestInvite: false,
|
|
permanent: false,
|
|
private: '', // part of instance tag
|
|
hidden: '', // part of instance tag
|
|
nonce: '', // only present when you're the owner
|
|
strict: false, // deprecated
|
|
displayName: null,
|
|
groupAccessType: null, // only present with group instance type
|
|
roleRestricted: false, // only present with group instance type
|
|
instancePersistenceEnabled: null,
|
|
playerPersistenceEnabled: null,
|
|
ageGate: null,
|
|
// VRCX
|
|
$fetchedAt: '',
|
|
$disabledContentSettings: [],
|
|
...json
|
|
};
|
|
state.cachedInstances.set(ref.id, ref);
|
|
} else {
|
|
Object.assign(ref, json);
|
|
}
|
|
ref.$location = parseLocation(ref.location);
|
|
if (ref.world?.id) {
|
|
worldRequest
|
|
.getCachedWorld({
|
|
worldId: ref.world.id
|
|
})
|
|
.then((args) => {
|
|
ref.world = args.ref;
|
|
return args;
|
|
});
|
|
}
|
|
ref.$disabledContentSettings = [];
|
|
if (json.contentSettings && Object.keys(json.contentSettings).length) {
|
|
for (const setting of instanceContentSettings) {
|
|
if (
|
|
typeof json.contentSettings[setting] === 'undefined' ||
|
|
json.contentSettings[setting] === true
|
|
) {
|
|
continue;
|
|
}
|
|
ref.$disabledContentSettings.push(setting);
|
|
}
|
|
}
|
|
if (
|
|
userStore.userDialog.visible &&
|
|
userStore.userDialog.ref.$location.tag === ref.id
|
|
) {
|
|
userStore.applyUserDialogLocation();
|
|
}
|
|
if (
|
|
worldStore.worldDialog.visible &&
|
|
worldStore.worldDialog.id === ref.worldId
|
|
) {
|
|
applyWorldDialogInstances();
|
|
}
|
|
if (
|
|
groupStore.groupDialog.visible &&
|
|
groupStore.groupDialog.id === ref.ownerId
|
|
) {
|
|
applyGroupDialogInstances();
|
|
}
|
|
return ref;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} worldId
|
|
* @param {any} options
|
|
* @returns {Promise<{json: *, params}|null>}
|
|
*/
|
|
async function createNewInstance(worldId = '', options) {
|
|
let D = options;
|
|
|
|
if (!D) {
|
|
D = {
|
|
loading: false,
|
|
accessType: await configRepository.getString(
|
|
'instanceDialogAccessType',
|
|
'public'
|
|
),
|
|
region: await configRepository.getString(
|
|
'instanceRegion',
|
|
'US West'
|
|
),
|
|
worldId: worldId,
|
|
groupId: await configRepository.getString(
|
|
'instanceDialogGroupId',
|
|
''
|
|
),
|
|
groupAccessType: await configRepository.getString(
|
|
'instanceDialogGroupAccessType',
|
|
'plus'
|
|
),
|
|
ageGate: await configRepository.getBool(
|
|
'instanceDialogAgeGate',
|
|
false
|
|
),
|
|
queueEnabled: await configRepository.getBool(
|
|
'instanceDialogQueueEnabled',
|
|
true
|
|
),
|
|
roleIds: [],
|
|
groupRef: {}
|
|
};
|
|
}
|
|
|
|
let type = 'public';
|
|
let canRequestInvite = false;
|
|
switch (D.accessType) {
|
|
case 'friends':
|
|
type = 'friends';
|
|
break;
|
|
case 'friends+':
|
|
type = 'hidden';
|
|
break;
|
|
case 'invite':
|
|
type = 'private';
|
|
break;
|
|
case 'invite+':
|
|
type = 'private';
|
|
canRequestInvite = true;
|
|
break;
|
|
case 'group':
|
|
type = 'group';
|
|
break;
|
|
}
|
|
let region = 'us';
|
|
if (D.region === 'US East') {
|
|
region = 'use';
|
|
} else if (D.region === 'Europe') {
|
|
region = 'eu';
|
|
} else if (D.region === 'Japan') {
|
|
region = 'jp';
|
|
}
|
|
const params = {
|
|
type,
|
|
canRequestInvite,
|
|
worldId: D.worldId,
|
|
ownerId: userStore.currentUser.id,
|
|
region
|
|
};
|
|
if (type === 'group') {
|
|
params.groupAccessType = D.groupAccessType;
|
|
params.ownerId = D.groupId;
|
|
params.queueEnabled = D.queueEnabled;
|
|
if (D.groupAccessType === 'members') {
|
|
params.roleIds = D.roleIds;
|
|
}
|
|
}
|
|
if (
|
|
D.ageGate &&
|
|
type === 'group' &&
|
|
hasGroupPermission(D.groupRef, 'group-instance-age-gated-create')
|
|
) {
|
|
params.ageGate = true;
|
|
}
|
|
try {
|
|
const args = await instanceRequest.createInstance(params);
|
|
return args;
|
|
} catch (err) {
|
|
console.error(err);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function applyWorldDialogInstances() {
|
|
let ref;
|
|
let instance;
|
|
const D = worldStore.worldDialog;
|
|
if (!D.visible) {
|
|
return;
|
|
}
|
|
const instances = {};
|
|
if (D.ref.instances) {
|
|
for (instance of D.ref.instances) {
|
|
// instance = [ instanceId, occupants ]
|
|
const instanceId = instance[0];
|
|
instances[instanceId] = {
|
|
id: instanceId,
|
|
tag: `${D.id}:${instanceId}`,
|
|
$location: {},
|
|
friendCount: 0,
|
|
users: [],
|
|
shortName: '',
|
|
ref: {}
|
|
};
|
|
}
|
|
}
|
|
const { instanceId, shortName } = D.$location;
|
|
if (instanceId && typeof instances[instanceId] === 'undefined') {
|
|
instances[instanceId] = {
|
|
id: instanceId,
|
|
tag: `${D.id}:${instanceId}`,
|
|
$location: {},
|
|
friendCount: 0,
|
|
users: [],
|
|
shortName,
|
|
ref: {}
|
|
};
|
|
}
|
|
const cachedCurrentUser = userStore.cachedUsers.get(
|
|
userStore.currentUser.id
|
|
);
|
|
const lastLocation$ = cachedCurrentUser.$location;
|
|
const playersInInstance = locationStore.lastLocation.playerList;
|
|
if (lastLocation$.worldId === D.id && playersInInstance.size > 0) {
|
|
// pull instance json from cache
|
|
const friendsInInstance = locationStore.lastLocation.friendList;
|
|
instance = {
|
|
id: lastLocation$.instanceId,
|
|
tag: lastLocation$.tag,
|
|
$location: {},
|
|
friendCount: friendsInInstance.size,
|
|
users: [],
|
|
shortName: '',
|
|
ref: {}
|
|
};
|
|
instances[instance.id] = instance;
|
|
for (const friend of friendsInInstance.values()) {
|
|
// if friend isn't in instance add them
|
|
const addUser = !instance.users.some(function (user) {
|
|
return friend.userId === user.id;
|
|
});
|
|
if (addUser) {
|
|
ref = userStore.cachedUsers.get(friend.userId);
|
|
if (typeof ref !== 'undefined') {
|
|
instance.users.push(ref);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (const friend of friendStore.friends.values()) {
|
|
const { ref } = friend;
|
|
if (
|
|
typeof ref === 'undefined' ||
|
|
typeof ref.$location === 'undefined' ||
|
|
ref.$location.worldId !== D.id ||
|
|
(ref.$location.instanceId === lastLocation$.instanceId &&
|
|
playersInInstance.size > 0 &&
|
|
ref.location !== 'traveling')
|
|
) {
|
|
continue;
|
|
}
|
|
if (ref.location === locationStore.lastLocation.location) {
|
|
// don't add friends to currentUser gameLog instance (except when traveling)
|
|
continue;
|
|
}
|
|
const { instanceId } = ref.$location;
|
|
instance = instances[instanceId];
|
|
if (typeof instance === 'undefined') {
|
|
instance = {
|
|
id: instanceId,
|
|
tag: `${D.id}:${instanceId}`,
|
|
$location: {},
|
|
friendCount: 0,
|
|
users: [],
|
|
shortName: '',
|
|
ref: {}
|
|
};
|
|
instances[instanceId] = instance;
|
|
}
|
|
instance.users.push(ref);
|
|
}
|
|
ref = userStore.cachedUsers.get(userStore.currentUser.id);
|
|
if (typeof ref !== 'undefined' && ref.$location.worldId === D.id) {
|
|
const { instanceId } = ref.$location;
|
|
instance = instances[instanceId];
|
|
if (typeof instance === 'undefined') {
|
|
instance = {
|
|
id: instanceId,
|
|
tag: `${D.id}:${instanceId}`,
|
|
$location: {},
|
|
friendCount: 0,
|
|
users: [],
|
|
shortName: '',
|
|
ref: {}
|
|
};
|
|
instances[instanceId] = instance;
|
|
}
|
|
instance.users.push(ref); // add self
|
|
}
|
|
const rooms = [];
|
|
for (instance of Object.values(instances)) {
|
|
// due to references on callback of API.getUser()
|
|
// this should be block scope variable
|
|
const L = parseLocation(`${D.id}:${instance.id}`);
|
|
instance.location = L.tag;
|
|
if (!L.shortName) {
|
|
L.shortName = instance.shortName;
|
|
}
|
|
instance.$location = L;
|
|
if (L.userId) {
|
|
ref = userStore.cachedUsers.get(L.userId);
|
|
if (typeof ref === 'undefined') {
|
|
userRequest
|
|
.getUser({
|
|
userId: L.userId
|
|
})
|
|
.then((args) => {
|
|
Vue.set(L, 'user', args.ref);
|
|
return args;
|
|
})
|
|
.catch((error) => {
|
|
console.error('Error fetching user:', error);
|
|
});
|
|
} else {
|
|
L.user = ref;
|
|
}
|
|
}
|
|
if (instance.friendCount === 0) {
|
|
instance.friendCount = instance.users.length;
|
|
}
|
|
if (appearanceSettingsStore.instanceUsersSortAlphabetical) {
|
|
instance.users.sort(compareByDisplayName);
|
|
} else {
|
|
instance.users.sort(compareByLocationAt);
|
|
}
|
|
rooms.push(instance);
|
|
}
|
|
// get instance from cache
|
|
for (const room of rooms) {
|
|
ref = state.cachedInstances.get(room.tag);
|
|
if (typeof ref !== 'undefined') {
|
|
room.ref = ref;
|
|
}
|
|
}
|
|
rooms.sort(function (a, b) {
|
|
// sort selected and current instance to top
|
|
if (
|
|
b.location === D.$location.tag ||
|
|
b.location === lastLocation$.tag
|
|
) {
|
|
// sort selected instance above current instance
|
|
if (a.location === D.$location.tag) {
|
|
return -1;
|
|
}
|
|
return 1;
|
|
}
|
|
if (
|
|
a.location === D.$location.tag ||
|
|
a.location === lastLocation$.tag
|
|
) {
|
|
// sort selected instance above current instance
|
|
if (b.location === D.$location.tag) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
// sort by number of users when no friends in instance
|
|
if (a.users.length === 0 && b.users.length === 0) {
|
|
if (a.ref?.userCount < b.ref?.userCount) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
// sort by number of friends in instance
|
|
if (a.users.length < b.users.length) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
});
|
|
D.rooms = rooms;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {object} inputInstances
|
|
*/
|
|
function applyGroupDialogInstances(inputInstances) {
|
|
let ref;
|
|
let instance;
|
|
const D = groupStore.groupDialog;
|
|
if (!D.visible) {
|
|
return;
|
|
}
|
|
const instances = {};
|
|
for (instance of D.instances) {
|
|
instances[instance.tag] = {
|
|
...instance,
|
|
friendCount: 0,
|
|
users: []
|
|
};
|
|
}
|
|
if (typeof inputInstances !== 'undefined') {
|
|
for (instance of inputInstances) {
|
|
instances[instance.location] = {
|
|
id: instance.instanceId,
|
|
tag: instance.location,
|
|
$location: {},
|
|
friendCount: 0,
|
|
users: [],
|
|
shortName: instance.shortName,
|
|
ref: instance
|
|
};
|
|
}
|
|
}
|
|
const cachedCurrentUser = userStore.cachedUsers.get(
|
|
userStore.currentUser.id
|
|
);
|
|
const lastLocation$ = cachedCurrentUser.$location;
|
|
const currentLocation = lastLocation$.tag;
|
|
const playersInInstance = locationStore.lastLocation.playerList;
|
|
if (lastLocation$.groupId === D.id && playersInInstance.size > 0) {
|
|
const friendsInInstance = locationStore.lastLocation.friendList;
|
|
instance = {
|
|
id: lastLocation$.instanceId,
|
|
tag: currentLocation,
|
|
$location: {},
|
|
friendCount: friendsInInstance.size,
|
|
users: [],
|
|
shortName: '',
|
|
ref: {}
|
|
};
|
|
instances[currentLocation] = instance;
|
|
for (const friend of friendsInInstance.values()) {
|
|
// if friend isn't in instance add them
|
|
const addUser = !instance.users.some(function (user) {
|
|
return friend.userId === user.id;
|
|
});
|
|
if (addUser) {
|
|
ref = userStore.cachedUsers.get(friend.userId);
|
|
if (typeof ref !== 'undefined') {
|
|
instance.users.push(ref);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (const friend of friendStore.friends.values()) {
|
|
const { ref } = friend;
|
|
if (
|
|
typeof ref === 'undefined' ||
|
|
typeof ref.$location === 'undefined' ||
|
|
ref.$location.groupId !== D.id ||
|
|
(ref.$location.instanceId === lastLocation$.instanceId &&
|
|
playersInInstance.size > 0 &&
|
|
ref.location !== 'traveling')
|
|
) {
|
|
continue;
|
|
}
|
|
if (ref.location === locationStore.lastLocation.location) {
|
|
// don't add friends to currentUser gameLog instance (except when traveling)
|
|
continue;
|
|
}
|
|
const { instanceId, tag } = ref.$location;
|
|
instance = instances[tag];
|
|
if (typeof instance === 'undefined') {
|
|
instance = {
|
|
id: instanceId,
|
|
tag,
|
|
$location: {},
|
|
friendCount: 0,
|
|
users: [],
|
|
shortName: '',
|
|
ref: {}
|
|
};
|
|
instances[tag] = instance;
|
|
}
|
|
instance.users.push(ref);
|
|
}
|
|
ref = userStore.cachedUsers.get(userStore.currentUser.id);
|
|
if (typeof ref !== 'undefined' && ref.$location.groupId === D.id) {
|
|
const { instanceId, tag } = ref.$location;
|
|
instance = instances[tag];
|
|
if (typeof instance === 'undefined') {
|
|
instance = {
|
|
id: instanceId,
|
|
tag,
|
|
$location: {},
|
|
friendCount: 0,
|
|
users: [],
|
|
shortName: '',
|
|
ref: {}
|
|
};
|
|
instances[tag] = instance;
|
|
}
|
|
instance.users.push(ref); // add self
|
|
}
|
|
const rooms = [];
|
|
for (instance of Object.values(instances)) {
|
|
// due to references on callback of API.getUser()
|
|
// this should be block scope variable
|
|
const L = parseLocation(instance.tag);
|
|
instance.location = instance.tag;
|
|
instance.$location = L;
|
|
if (instance.friendCount === 0) {
|
|
instance.friendCount = instance.users.length;
|
|
}
|
|
if (appearanceSettingsStore.instanceUsersSortAlphabetical) {
|
|
instance.users.sort(compareByDisplayName);
|
|
} else {
|
|
instance.users.sort(compareByLocationAt);
|
|
}
|
|
rooms.push(instance);
|
|
}
|
|
// get instance
|
|
for (const room of rooms) {
|
|
ref = cachedInstances.value.get(room.tag);
|
|
if (typeof ref !== 'undefined') {
|
|
room.ref = ref;
|
|
} else if (isRealInstance(room.tag)) {
|
|
instanceRequest.getInstance({
|
|
worldId: room.$location.worldId,
|
|
instanceId: room.$location.instanceId
|
|
});
|
|
}
|
|
}
|
|
rooms.sort(function (a, b) {
|
|
// sort current instance to top
|
|
if (b.location === currentLocation) {
|
|
return 1;
|
|
}
|
|
if (a.location === currentLocation) {
|
|
return -1;
|
|
}
|
|
// sort by number of users when no friends in instance
|
|
if (a.users.length === 0 && b.users.length === 0) {
|
|
if (a.ref?.userCount < b.ref?.userCount) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
}
|
|
// sort by number of friends in instance
|
|
if (a.users.length < b.users.length) {
|
|
return 1;
|
|
}
|
|
return -1;
|
|
});
|
|
D.instances = rooms;
|
|
}
|
|
|
|
function removeAllQueuedInstances() {
|
|
state.queuedInstances.forEach((ref) => {
|
|
$app.$message({
|
|
message: `Removed instance ${ref.$worldName} from queue`,
|
|
type: 'info'
|
|
});
|
|
ref.$msgBox?.close();
|
|
});
|
|
state.queuedInstances.clear();
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} instanceId
|
|
*/
|
|
function removeQueuedInstance(instanceId) {
|
|
const ref = state.queuedInstances.get(instanceId);
|
|
if (typeof ref !== 'undefined') {
|
|
ref.$msgBox.close();
|
|
state.queuedInstances.delete(instanceId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} instanceId
|
|
*/
|
|
function applyQueuedInstance(instanceId) {
|
|
state.queuedInstances.forEach((ref) => {
|
|
if (ref.location !== instanceId) {
|
|
$app.$message({
|
|
message: t('message.instance.removed_form_queue', {
|
|
worldName: ref.$worldName
|
|
}),
|
|
type: 'info'
|
|
});
|
|
ref.$msgBox?.close();
|
|
state.queuedInstances.delete(ref.location);
|
|
}
|
|
});
|
|
if (!instanceId) {
|
|
return;
|
|
}
|
|
if (!state.queuedInstances.has(instanceId)) {
|
|
const L = parseLocation(instanceId);
|
|
if (L.isRealInstance) {
|
|
instanceRequest
|
|
.getInstance({
|
|
worldId: L.worldId,
|
|
instanceId: L.instanceId
|
|
})
|
|
.then((args) => {
|
|
if (args.json?.queueSize) {
|
|
instanceQueueUpdate(
|
|
instanceId,
|
|
args.json?.queueSize,
|
|
args.json?.queueSize
|
|
);
|
|
}
|
|
})
|
|
.catch((error) => {
|
|
console.error(
|
|
'Error fetching instance data for queue:',
|
|
error
|
|
);
|
|
});
|
|
}
|
|
instanceQueueUpdate(instanceId, 0, 0);
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} instanceId
|
|
*/
|
|
function instanceQueueReady(instanceId) {
|
|
const ref = state.queuedInstances.get(instanceId);
|
|
if (typeof ref !== 'undefined') {
|
|
ref.$msgBox.close();
|
|
state.queuedInstances.delete(instanceId);
|
|
}
|
|
const L = parseLocation(instanceId);
|
|
const group = groupStore.cachedGroups.get(L.groupId);
|
|
const groupName = group?.name ?? '';
|
|
const worldName = ref?.$worldName ?? '';
|
|
const location = displayLocation(instanceId, worldName, groupName);
|
|
$app.$message({
|
|
message: `Instance ready to join ${location}`,
|
|
type: 'success'
|
|
});
|
|
const noty = {
|
|
created_at: new Date().toJSON(),
|
|
type: 'group.queueReady',
|
|
imageUrl: group?.iconUrl,
|
|
message: `Instance ready to join ${location}`,
|
|
location: instanceId,
|
|
groupName,
|
|
worldName
|
|
};
|
|
if (
|
|
notificationStore.notificationTable.filters[0].value.length === 0 ||
|
|
notificationStore.notificationTable.filters[0].value.includes(
|
|
noty.type
|
|
)
|
|
) {
|
|
uiStore.notifyMenu('notification');
|
|
}
|
|
notificationStore.queueNotificationNoty(noty);
|
|
notificationStore.notificationTable.data.push(noty);
|
|
sharedFeedStore.updateSharedFeed(true);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {string} instanceId
|
|
* @param {number} position
|
|
* @param {number} queueSize
|
|
* @returns {Promise<void>}
|
|
*/
|
|
async function instanceQueueUpdate(instanceId, position, queueSize) {
|
|
let ref = state.queuedInstances.get(instanceId);
|
|
if (typeof ref === 'undefined') {
|
|
ref = {
|
|
$msgBox: null,
|
|
$groupName: '',
|
|
$worldName: '',
|
|
location: instanceId,
|
|
position: 0,
|
|
queueSize: 0,
|
|
updatedAt: 0
|
|
};
|
|
}
|
|
ref.position = position;
|
|
ref.queueSize = queueSize;
|
|
ref.updatedAt = Date.now();
|
|
if (!ref.$msgBox || ref.$msgBox.closed) {
|
|
ref.$msgBox = $app.$message({
|
|
message: '',
|
|
type: 'info',
|
|
duration: 0,
|
|
showClose: true,
|
|
customClass: 'vrc-instance-queue-message'
|
|
});
|
|
}
|
|
if (!ref.$groupName) {
|
|
ref.$groupName = await getGroupName(instanceId);
|
|
}
|
|
if (!ref.$worldName) {
|
|
ref.$worldName = await getWorldName(instanceId);
|
|
}
|
|
const location = displayLocation(
|
|
instanceId,
|
|
ref.$worldName,
|
|
ref.$groupName
|
|
);
|
|
ref.$msgBox.message = `You are in position ${ref.position} of ${ref.queueSize} in the queue for ${location} `;
|
|
state.queuedInstances.set(instanceId, ref);
|
|
// workerTimers.setTimeout(this.instanceQueueTimeout, 3600000);
|
|
}
|
|
|
|
function getCurrentInstanceUserList() {
|
|
if (!watchState.isFriendsLoaded) {
|
|
return;
|
|
}
|
|
if (state.updatePlayerListTimer) {
|
|
state.updatePlayerListPending = true;
|
|
} else {
|
|
updatePlayerListExecute();
|
|
state.updatePlayerListTimer = setTimeout(() => {
|
|
if (state.updatePlayerListPending) {
|
|
updatePlayerListExecute();
|
|
}
|
|
state.updatePlayerListTimer = null;
|
|
}, 150);
|
|
}
|
|
}
|
|
|
|
function updatePlayerListExecute() {
|
|
try {
|
|
updatePlayerListDebounce();
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
state.updatePlayerListTimer = null;
|
|
state.updatePlayerListPending = false;
|
|
}
|
|
|
|
function updatePlayerListDebounce() {
|
|
const users = [];
|
|
const pushUser = function (ref) {
|
|
let photonId = '';
|
|
let isFriend = false;
|
|
photonStore.photonLobbyCurrent.forEach((ref1, id) => {
|
|
if (typeof ref1 !== 'undefined') {
|
|
if (
|
|
(typeof ref.id !== 'undefined' &&
|
|
typeof ref1.id !== 'undefined' &&
|
|
ref1.id === ref.id) ||
|
|
(typeof ref.displayName !== 'undefined' &&
|
|
typeof ref1.displayName !== 'undefined' &&
|
|
ref1.displayName === ref.displayName)
|
|
) {
|
|
photonId = id;
|
|
}
|
|
}
|
|
});
|
|
let isMaster = false;
|
|
if (
|
|
photonStore.photonLobbyMaster !== 0 &&
|
|
photonId === photonStore.photonLobbyMaster
|
|
) {
|
|
isMaster = true;
|
|
}
|
|
let isModerator = false;
|
|
const lobbyJointime = photonStore.photonLobbyJointime.get(photonId);
|
|
let inVRMode = null;
|
|
let groupOnNameplate = '';
|
|
if (typeof lobbyJointime !== 'undefined') {
|
|
inVRMode = lobbyJointime.inVRMode;
|
|
groupOnNameplate = lobbyJointime.groupOnNameplate;
|
|
isModerator = lobbyJointime.canModerateInstance;
|
|
}
|
|
// if (groupOnNameplate) {
|
|
// API.getCachedGroup({
|
|
// groupId: groupOnNameplate
|
|
// }).then((args) => {
|
|
// groupOnNameplate = args.ref.name;
|
|
// });
|
|
// }
|
|
let timeoutTime = 0;
|
|
if (typeof ref.id !== 'undefined') {
|
|
isFriend = ref.isFriend;
|
|
if (
|
|
photonStore.timeoutHudOverlayFilter === 'VIP' ||
|
|
photonStore.timeoutHudOverlayFilter === 'Friends'
|
|
) {
|
|
photonStore.photonLobbyTimeout.forEach((ref1) => {
|
|
if (ref1.userId === ref.id) {
|
|
timeoutTime = ref1.time;
|
|
}
|
|
});
|
|
} else {
|
|
photonStore.photonLobbyTimeout.forEach((ref1) => {
|
|
if (ref1.displayName === ref.displayName) {
|
|
timeoutTime = ref1.time;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
users.push({
|
|
ref,
|
|
displayName: ref.displayName,
|
|
timer: ref.$location_at,
|
|
$trustSortNum: ref.$trustSortNum ?? 0,
|
|
photonId,
|
|
isMaster,
|
|
isModerator,
|
|
inVRMode,
|
|
groupOnNameplate,
|
|
isFriend,
|
|
timeoutTime
|
|
});
|
|
// get block, mute
|
|
};
|
|
|
|
const playersInInstance = locationStore.lastLocation.playerList;
|
|
if (playersInInstance.size > 0) {
|
|
let ref = userStore.cachedUsers.get(userStore.currentUser.id);
|
|
if (typeof ref !== 'undefined' && playersInInstance.has(ref.id)) {
|
|
pushUser(ref);
|
|
}
|
|
for (const player of playersInInstance.values()) {
|
|
// if friend isn't in instance add them
|
|
if (player.displayName === userStore.currentUser.displayName) {
|
|
continue;
|
|
}
|
|
const addUser = !users.some(function (user) {
|
|
return player.displayName === user.displayName;
|
|
});
|
|
if (addUser) {
|
|
ref = userStore.cachedUsers.get(player.userId);
|
|
if (typeof ref !== 'undefined') {
|
|
pushUser(ref);
|
|
} else {
|
|
let { joinTime } =
|
|
locationStore.lastLocation.playerList.get(
|
|
player.userId
|
|
);
|
|
if (!joinTime) {
|
|
joinTime = Date.now();
|
|
}
|
|
ref = {
|
|
// if userId is missing just push displayName
|
|
displayName: player.displayName,
|
|
$location_at: joinTime,
|
|
$online_for: joinTime
|
|
};
|
|
pushUser(ref);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
state.currentInstanceUserList.data = users;
|
|
}
|
|
|
|
// $app.methods.instanceQueueClear = function () {
|
|
// // remove all instances from queue
|
|
// state.queuedInstances.forEach((ref) => {
|
|
// ref.$msgBox.close();
|
|
// state.queuedInstances.delete(ref.location);
|
|
// });
|
|
// };
|
|
|
|
return {
|
|
state,
|
|
cachedInstances,
|
|
currentInstanceWorld,
|
|
currentInstanceLocation,
|
|
queuedInstances,
|
|
previousInstancesInfoDialogVisible,
|
|
previousInstancesInfoDialogInstanceId,
|
|
instanceJoinHistory,
|
|
currentInstanceUserList,
|
|
applyInstance,
|
|
updateCurrentInstanceWorld,
|
|
createNewInstance,
|
|
applyWorldDialogInstances,
|
|
applyGroupDialogInstances,
|
|
removeAllQueuedInstances,
|
|
removeQueuedInstance,
|
|
applyQueuedInstance,
|
|
instanceQueueReady,
|
|
instanceQueueUpdate,
|
|
showPreviousInstancesInfoDialog,
|
|
addInstanceJoinHistory,
|
|
getCurrentInstanceUserList,
|
|
getInstanceJoinHistory
|
|
};
|
|
});
|