Skip to content

Commit 65790f9

Browse files
Updated tutorials related to instances (#144)
Changes to Pipeline events Added support for Group Public, Group+, and Group instances to instance generator widget
1 parent 4ea2baf commit 65790f9

File tree

3 files changed

+118
-40
lines changed

3 files changed

+118
-40
lines changed

content/tutorials/instances.markdown

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ category: general
44
difficulty: medium
55
---
66

7-
Instances are parallell "rooms" or "lobbies" of a world.
7+
Instances are parallel "rooms" or "lobbies" of a world.
88
Each instance can only hold a limited number of users, but there is no limit on how many instances can exist of a world.
99
Instances are uniquely identified by the combined World ID and Instance ID.
1010

11+
*Note: as of 2024-05-02, VRChat indicated in [a Developer Update](https://ask.vrchat.com/t/developer-update-2-may-2024/24284#changes-to-instance-apis-and-auto-creation-13) an eventual intent to replace the current system with a UUID-ish system similar to User IDs*
12+
1113
## Instance Generator
1214

1315
{{< instance_generator >}}
@@ -37,3 +39,12 @@ USA, West | San José | us
3739
USA, East | Washington D.C. | use
3840
Europe | Amsterdam | eu
3941
Japan | Tokyo | jp
42+
43+
### Special Values
44+
45+
The VRChat API has several sentinel values for location strings:
46+
47+
- `""` Pseudo-null value
48+
- `"offline"` Implies a user currently is not either running the VRChat client or connected to the Pipeline (e.g., browser tab open)
49+
- `"traveling"` Indicates a user's client is travelling between instances (e.g., downloading world, synchronizing world state)
50+
- `"private"` Indicates a user's location is not visible to the currently logged-in user. (e.g., Ask Me/Do Not Disturb status, Invite/Invite+/Group instance)

content/tutorials/websocket.markdown

Lines changed: 52 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,36 @@ It is possible to be connected from multiple locations at the same time. All cli
2828
> }
2929
> ```
3030
31+
## Note on Enumerations
32+
33+
Several JSON string values present in both Websocket API messages and HTTP API responses appear to be contained in predictably consistent sets.
34+
Throughout both APIs, the empty string `""` is returned in places (including the immediately) where it would seem otherwise reasonable to have a `null` or undefined value.
35+
In this part of the documentation, the following `":identifier"`s will be used to describe possible enumeration-ish values:
36+
37+
- `":locationString"`
38+
- `""` Pseudo-null value
39+
- `"offline"` Implies a user currently is not either running the VRChat client or connected to the Pipeline (e.g., browser tab open)
40+
- `"traveling"` Indicates a user's client is travelling between instances (e.g., downloading world, synchronizing world state)
41+
- `"private"` Indicates a user's location is not visible to the currently logged-in user. (e.g., Ask Me/Do Not Disturb status, Invite/Invite+/Group instance)
42+
- *other values* An actual location (see https://vrchatapi.github.io/tutorials/instances/)
43+
- `":platformString"`
44+
- `""` Pseudo-null value
45+
- `"standalonewindows"`
46+
- `"android"`
47+
- `"web"` User is on a https://vrchat.com/home page
48+
- *other values* Some other platform or third-party application (e.g., `"ios"` could be the value for a future build on some Apple devices)
49+
- `":contentRefreshContentTypeEnum"`
50+
- `"gallery"`
51+
- `"icon"`
52+
- `"emoji"`
53+
- `"avatar"`
54+
- `"world"`
55+
- *other values* Some other user-uploaded content
56+
- `":contentRefreshActionTypeEnum"`
57+
- `"created"`
58+
- `"deleted"`
59+
- *other values* Not expected
60+
3161
## Events
3262
3363
### Notification Events
@@ -209,13 +239,13 @@ A "`friend-online`" event is sent when one of the user's friends has gone online
209239
"type": "friend-online",
210240
"content": {
211241
"userId": ":userId",
242+
"platform": ":platformString",
243+
"location": ":locationString",
244+
"canRequestInvite": <boolean>,
212245
"user": {
213246
// <User Object>, See return data of User API:
214247
// https://vrchatapi.github.io/docs/api/#get-/users/-userId-
215-
},
216-
"location": ":locationString", // Refer to https://vrchatapi.github.io/tutorials/instances/
217-
"instance": ":instanceId", // This is locationString without the World ID part.
218-
"canRequestInvite": <boolean>
248+
}
219249
}
220250
}
221251
```
@@ -228,6 +258,7 @@ A "`friend-active`" event is sent when one of the user's friends is active on th
228258
"type": "friend-active",
229259
"content": {
230260
"userid": ":userId",
261+
"platform": ":platformString", // Appears only to be "web" from these events
231262
"user": {
232263
// <User Object>, See return data of User API:
233264
// https://vrchatapi.github.io/docs/api/#get-/users/-userId-
@@ -243,7 +274,8 @@ A "`friend-offline`" event is sent when one of the user's friends has gone offli
243274
{
244275
"type": "friend-offline",
245276
"content": {
246-
"userId": ":userId"
277+
"userId": ":userId",
278+
"platform": ":platformString", // Appears only to be empty "" from these events
247279
}
248280
}
249281
```
@@ -266,22 +298,21 @@ A "`friend-update`" event is sent when something about one of the user's friends
266298

267299

268300
#### friend-location
269-
A "`friend-location`" event is sent when one of the user's friends has changed instances. Note that the "`world`" field will be an empty object when the friend is on orange/red, or is in a private world.
301+
A "`friend-location`" event is sent when one of the user's friends has changed instances. Note that the `worldId` field will be `"private"` when the friend is on orange/red, or is in a private world.
270302

271303
```json
272304
{
273305
"type": "friend-location",
274306
"content": {
275307
"userId": ":userId",
308+
"location": ":locationString",
309+
"travelingToLocation": ":locationString", // normally empty "", but when the above "location" is "traveling", this contains the imminent destination
310+
"worldId": ":worldId",
311+
"canRequestInvite": <boolean>,
276312
"user": {
277313
// <User Object>, See return data of User API:
278314
// https://vrchatapi.github.io/docs/api/#get-/users/-userId-
279-
},
280-
"location": ":locationString", // Refer to https://vrchatapi.github.io/tutorials/instances/
281-
"travelingToLocation": ":locationString", // Refer to https://vrchatapi.github.io/tutorials/instances/
282-
"instance": ":instanceId", // This is locationString without the World ID part.
283-
"worldId": ":worldId", // wlrd_...
284-
"canRequestInvite": <boolean>
315+
}
285316
}
286317
}
287318
```
@@ -335,9 +366,9 @@ A "`user-location`" event is sent when the user has changed instances.
335366
// <User Object>, See return data of User API:
336367
// https://vrchatapi.github.io/docs/api/#get-/users/-userId-
337368
},
338-
"location": ":locationString", // Refer to https://vrchatapi.github.io/tutorials/instances/
369+
"location": ":locationString",
339370
"instance": ":instanceId", // This is locationString without the World ID part.
340-
"worldId": ":worldId", // wlrd_...
371+
"worldId": ":worldId",
341372
"world": {
342373
// <World Object>, See return data of World API:
343374
// https://vrchatapi.github.io/docs/api/#get-/worlds/-worldId-
@@ -354,8 +385,8 @@ A "`content-refresh`" event is sent when the user adds or removes profile images
354385
{
355386
"type": "content-refresh",
356387
"content": {
357-
"contentType": ":contentRefreshContentTypeEnum", // One of: "gallery", "icon", "emoji", "avatar", "world", ???
358-
"actionType": ":contentRefreshActionTypeEnum" // One of: "created", "deleted", ???
388+
"contentType": ":contentRefreshContentTypeEnum",
389+
"actionType": ":contentRefreshActionTypeEnum"
359390
}
360391
}
361392
```
@@ -368,7 +399,7 @@ A "`instance-queue-joined`" event is sent when the user queues to join an instan
368399
{
369400
"type": "instance-queue-joined",
370401
"content": {
371-
"instanceLocation": ":locationString", // Refer to https://vrchatapi.github.io/tutorials/instances/
402+
"instanceLocation": ":locationString",
372403
"position": <number> // Integer position in queue
373404
}
374405
}
@@ -382,7 +413,7 @@ A "`instance-queue-ready`" event is sent when the user is at the front of the qu
382413
{
383414
"type": "instance-queue-ready",
384415
"content": {
385-
"instanceLocation": ":locationString", // Refer to https://vrchatapi.github.io/tutorials/instances/
416+
"instanceLocation": ":locationString",
386417
"expiry": ":dateTimeString" // Time at which priority will be lost
387418
}
388419
}
@@ -419,25 +450,15 @@ A "`group-left`" event is sent when the user has either left a group, or has bee
419450

420451

421452
#### group-member-updated
422-
A "`group-member-updated`" event is sent when something regarding the user's group membership changes. Note that the `member` object is **not** a full GroupMember object, even though it has similarities. It's missing `user`, `createdAt`, `bannedAt`, and `managerNotes`. It also has the extra `lastPostReadAt` field.
453+
A "`group-member-updated`" event is sent when something regarding the user's group membership changes. Note that the optional values in the `member` are as if "fetching a specific user", per the schema description.
423454

424455
```json
425456
{
426457
"type": "group-member-updated",
427458
"content": {
428459
"member": {
429-
"id": ":groupMemberId", // gmem_00000000-0000-0000-000000000000
430-
"groupId": ":groupId",
431-
"userId": ":userId",
432-
"isRepresenting": <boolean>,
433-
"roleIds": [
434-
":groupRoleId" // grol_00000000-0000-0000-000000000000
435-
],
436-
"joinedAt": ":dateTimeString", // yyyy-mm-ddThh:mm:ss.sssZ
437-
"membershipStatus": ":groupMembershipEnum", // One of: "member", ???
438-
"visibility": ":groupVisibilityEnum", // One of: "visible", ???
439-
"isSubscribedToAnnouncements": <boolean>,
440-
"lastPostReadAt": ":dateTimeString" // NOTE: WILL BE NULL if the user hasn't read any group posts
460+
// <GroupLimitedMember Object>, See return data of Groups API:
461+
// https://vrchatapi.github.io/docs/api/#get-/groups/-groupId-/members/-userId-
441462
}
442463
}
443464
}

layouts/shortcodes/instance_generator.html

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
<option value="friends">Friends</option>
4646
<option value="invite+">Invite+</option>
4747
<option value="invite">Invite</option>
48+
<option value="grouppublic">Group Public</option>
49+
<option value="group+">Group+</option>
50+
<option value="group">Group</option>
4851
</select>
4952
</div>
5053
</div>
@@ -66,7 +69,7 @@
6669
</div>
6770
<div class="col mt-0 mt-lg-3 mb-3 mb-lg-0">
6871
<span class="form-text">
69-
Must be a valid User ID.
72+
Must be a valid User ID or Group ID.
7073
</span>
7174
</div>
7275
</div>
@@ -143,6 +146,7 @@
143146
nonceElement.value = uuidv4();
144147
}
145148

149+
let ownerIdMatchesGroupID = (ownerId.match(/^(grp_[a-zA-Z0-9]{8}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{4}-[a-zA-Z0-9]{12})$/) || [])[1] == undefined;
146150
switch (instanceType) {
147151
case "public":
148152
ownerIdElement.disabled = true;
@@ -154,6 +158,20 @@
154158
case "invite":
155159
ownerIdElement.disabled = false;
156160
nonceElement.disabled = true;
161+
if (!ownerIdMatchesGroupID) {
162+
output.innerHTML = "<span class='text-red'>Invalid User ID.</span>";
163+
return;
164+
}
165+
break;
166+
case "grouppublic":
167+
case "group+":
168+
case "group":
169+
ownerIdElement.disabled = false;
170+
nonceElement.disabled = true;
171+
if (ownerIdMatchesGroupID) {
172+
output.innerHTML = "<span class='text-red'>Invalid Group ID.</span>";
173+
return;
174+
}
157175
break;
158176
}
159177

@@ -163,7 +181,8 @@
163181
}
164182

165183
let res_type = "";
166-
let res_hidden = "";
184+
let res_owner = "";
185+
let res_gaccess = "";
167186
let res_nonce = "";
168187
let res_canRequestInvite = false;
169188

@@ -177,38 +196,65 @@
177196
ownerIdElement.disabled = false;
178197
nonceElement.disabled = true;
179198
res_type = "hidden";
180-
res_hidden = ownerId;
199+
res_owner = ownerId;
181200
res_nonce = nonce;
182201
break;
183202
case "friends":
184203
ownerIdElement.disabled = false;
185204
nonceElement.disabled = true;
186205
res_type = "friends";
187-
res_hidden = ownerId;
206+
res_owner = ownerId;
188207
res_nonce = nonce;
189208
break;
190209
case "invite+":
191210
ownerIdElement.disabled = false;
192211
nonceElement.disabled = true;
193212
res_type = "private";
194213
res_canRequestInvite = true;
195-
res_hidden = ownerId;
214+
res_owner = ownerId;
196215
res_nonce = nonce;
197216
break;
198217
case "invite":
199218
ownerIdElement.disabled = false;
200219
nonceElement.disabled = true;
201220
res_type = "private";
202-
res_hidden = ownerId;
221+
res_owner = ownerId;
222+
res_nonce = nonce;
223+
break;
224+
case "grouppublic":
225+
ownerIdElement.disabled = false;
226+
nonceElement.disabled = true;
227+
res_type = "group";
228+
res_owner = ownerId;
229+
res_gaccess = "public";
230+
res_nonce = nonce;
231+
break;
232+
case "group+":
233+
ownerIdElement.disabled = false;
234+
nonceElement.disabled = true;
235+
res_type = "group";
236+
res_owner = ownerId;
237+
res_gaccess = "plus";
238+
res_nonce = nonce;
239+
break;
240+
case "group":
241+
ownerIdElement.disabled = false;
242+
nonceElement.disabled = true;
243+
res_type = "group";
244+
res_owner = ownerId;
245+
res_gaccess = "members";
203246
res_nonce = nonce;
204247
break;
205248
}
206249

207250
let result = "";
208251
result += "worldId=<span class='text-danger'>" + worldId + "</span>";
209252
result += "&instanceId=<span class='text-orange'>" + instanceId + "</span>";
210-
if (res_hidden != "") {
211-
result += "<span class='text-green'>~" + res_type + "(<span class='text-blue'>" + res_hidden + "</span>)</span>";
253+
if (res_owner != "") {
254+
result += "<span class='text-green'>~" + res_type + "(<span class='text-blue'>" + res_owner + "</span>)</span>";
255+
}
256+
if (res_gaccess != "") {
257+
result += "<span class='text-green'>~groupAccessType(<span class='text-blue'>" + res_gaccess + "</span>)</span>";
212258
}
213259
if (res_canRequestInvite) {
214260
result += "<span class='text-green'>~canRequestInvite</span>";

0 commit comments

Comments
 (0)