@lootlog/sdk 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +261 -40
- package/dist/activity.d.ts +9 -2
- package/dist/activity.d.ts.map +1 -1
- package/dist/activity.js +2 -2
- package/dist/activity.js.map +1 -1
- package/dist/battlelog.d.ts +280 -5
- package/dist/battlelog.d.ts.map +1 -1
- package/dist/battlelog.js +11 -6
- package/dist/battlelog.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/main.d.ts +1457 -918
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +400 -284
- package/dist/main.js.map +1 -1
- package/dist/{mutators-Da_vMUva.d.ts → mutators-BaZDDfwh.d.ts} +2 -2
- package/dist/{mutators-Da_vMUva.d.ts.map → mutators-BaZDDfwh.d.ts.map} +1 -1
- package/dist/{mutators-CaBTxpi-.js → mutators-Dj5qBH3y.js} +2 -2
- package/dist/{mutators-CaBTxpi-.js.map → mutators-Dj5qBH3y.js.map} +1 -1
- package/dist/realtime.d.ts +427 -12
- package/dist/realtime.d.ts.map +1 -1
- package/dist/realtime.js +768 -471
- package/dist/realtime.js.map +1 -1
- package/dist/search.d.ts +247 -5
- package/dist/search.d.ts.map +1 -1
- package/dist/search.js +115 -2
- package/dist/search.js.map +1 -1
- package/dist/{transport-CIfewyM7.js → transport-Cn1V6y-o.js} +46 -8
- package/dist/transport-Cn1V6y-o.js.map +1 -0
- package/dist/{transport-BHbtsYo_.d.ts → transport-CoTymLxF.d.ts} +3 -1
- package/dist/transport-CoTymLxF.d.ts.map +1 -0
- package/openapi/activity.json +16 -1
- package/openapi/battlelog.json +805 -14
- package/openapi/main.json +5752 -4060
- package/openapi/search.json +721 -53
- package/package.json +4 -4
- package/dist/transport-BHbtsYo_.d.ts.map +0 -1
- package/dist/transport-CIfewyM7.js.map +0 -1
package/dist/realtime.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Result, Schema, SchemaTransformation } from "effect";
|
|
2
2
|
import * as Schema$1 from "effect/Schema";
|
|
3
3
|
import { TaggedError } from "effect/Schema";
|
|
4
|
+
import * as SchemaTransformation$1 from "effect/SchemaTransformation";
|
|
4
5
|
import { decode, encode } from "@msgpack/msgpack";
|
|
5
6
|
//#region ../schema/src/npc-type.ts
|
|
6
7
|
const NpcTypeEnum = {
|
|
@@ -12,6 +13,7 @@ const NpcTypeEnum = {
|
|
|
12
13
|
EVENT_HERO: "EVENT_HERO",
|
|
13
14
|
COLOSSUS: "COLOSSUS",
|
|
14
15
|
TITAN: "TITAN",
|
|
16
|
+
HERO_TRACKER: "HERO_TRACKER",
|
|
15
17
|
NPC: "NPC"
|
|
16
18
|
};
|
|
17
19
|
const NpcTypeSchema = Schema.Literals([
|
|
@@ -23,6 +25,7 @@ const NpcTypeSchema = Schema.Literals([
|
|
|
23
25
|
NpcTypeEnum.EVENT_HERO,
|
|
24
26
|
NpcTypeEnum.COLOSSUS,
|
|
25
27
|
NpcTypeEnum.TITAN,
|
|
28
|
+
NpcTypeEnum.HERO_TRACKER,
|
|
26
29
|
NpcTypeEnum.NPC
|
|
27
30
|
]);
|
|
28
31
|
//#endregion
|
|
@@ -139,7 +142,7 @@ const NPC_PERMISSIONS = {
|
|
|
139
142
|
Object.values(NPC_PERMISSIONS).flat();
|
|
140
143
|
new Set(NPC_PERMISSIONS.loots);
|
|
141
144
|
Object.values(Permission).sort();
|
|
142
|
-
|
|
145
|
+
Schema.is(NpcTypeSchema);
|
|
143
146
|
//#endregion
|
|
144
147
|
//#region ../schema/src/item-rarity.ts
|
|
145
148
|
const ItemRarityEnum = {
|
|
@@ -161,7 +164,7 @@ const LootSourceEnum = {
|
|
|
161
164
|
DIALOG: "DIALOG",
|
|
162
165
|
FIGHT: "FIGHT"
|
|
163
166
|
};
|
|
164
|
-
Schema.Literals([
|
|
167
|
+
const LootSourceSchema = Schema.Literals([
|
|
165
168
|
LootSourceEnum.LOOTBOX,
|
|
166
169
|
LootSourceEnum.DIALOG,
|
|
167
170
|
LootSourceEnum.FIGHT
|
|
@@ -263,18 +266,53 @@ Schema.Literals([
|
|
|
263
266
|
//#endregion
|
|
264
267
|
//#region ../schema/src/http-scalars.ts
|
|
265
268
|
const FiniteNumber = Schema$1.Number.check(Schema$1.isFinite().annotate({ expected: "a finite number" }));
|
|
266
|
-
const dateTimeString = (pattern) => Schema$1.String.annotate({ format: "date-time" }).check(Schema$1.isPattern(new RegExp(pattern)).annotate({ expected: `a string matching the RegExp ${pattern}` }));
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
269
|
+
const dateTimeString = (pattern) => Schema$1.String.annotate({ format: "date-time" }).check(Schema$1.isPattern(new RegExp(pattern, "u")).annotate({ expected: `a string matching the RegExp ${pattern}` }));
|
|
270
|
+
const dateTimePattern = (timeZonePattern) => `^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:${timeZonePattern}))$`;
|
|
271
|
+
/** Strict ISO-8601 UTC date-time pattern shared by HTTP and domain schemas. */
|
|
272
|
+
const DATE_TIME_STRING_PATTERN = dateTimePattern("Z");
|
|
273
|
+
const DateTimeString = dateTimeString(DATE_TIME_STRING_PATTERN);
|
|
274
|
+
dateTimeString(dateTimePattern("Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)"));
|
|
275
|
+
const NonEmptyString = Schema$1.String.check(Schema$1.isMinLength(1).annotate({ expected: "a value with a length of at least 1" }));
|
|
270
276
|
Schema$1.Json.annotate({ expected: "JSON value" });
|
|
271
|
-
Schema$1.Number.check(Schema$1.isInt().annotate({ expected: "an integer" })).check(Schema$1.isGreaterThanOrEqualTo(
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
277
|
+
const boundedInteger = (minimum, maximum) => Schema$1.Number.check(Schema$1.isInt().annotate({ expected: "an integer" })).check(Schema$1.isGreaterThanOrEqualTo(minimum).annotate({ expected: `a value greater than or equal to ${minimum}` })).check(Schema$1.isLessThanOrEqualTo(maximum).annotate({ expected: `a value less than or equal to ${maximum}` }));
|
|
278
|
+
boundedInteger(-9007199254740991, 9007199254740991);
|
|
279
|
+
const PositiveSafeInteger = boundedInteger(1, 9007199254740991);
|
|
280
|
+
const NonNegativeSafeInteger = boundedInteger(0, 9007199254740991);
|
|
281
|
+
const LevelFilter = boundedInteger(0, 500);
|
|
282
|
+
boundedInteger(1, 100);
|
|
283
|
+
/** Rejects a level filter whose lower bound exceeds its upper bound. */
|
|
284
|
+
const levelRangeCheck = (minKey, maxKey) => Schema$1.makeFilter((data) => {
|
|
285
|
+
const min = data[minKey];
|
|
286
|
+
const max = data[maxKey];
|
|
287
|
+
return min === void 0 || max === void 0 || min <= max ? void 0 : {
|
|
288
|
+
path: [minKey],
|
|
289
|
+
issue: `${minKey} must be <= ${maxKey}`
|
|
290
|
+
};
|
|
291
|
+
});
|
|
292
|
+
Schema$1.optionalKey(LevelFilter), Schema$1.optionalKey(LevelFilter), levelRangeCheck("npcLevelMin", "npcLevelMax");
|
|
293
|
+
Schema$1.Array(NpcTypeSchema);
|
|
294
|
+
Schema$1.Literals([
|
|
295
|
+
"24h",
|
|
296
|
+
"3d",
|
|
297
|
+
"7d",
|
|
298
|
+
"14d",
|
|
299
|
+
"30d",
|
|
300
|
+
"90d",
|
|
301
|
+
"180d",
|
|
302
|
+
"all"
|
|
303
|
+
]).annotate({ default: "7d" });
|
|
304
|
+
Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(64)).pipe(Schema$1.decode(SchemaTransformation$1.trim()), Schema$1.decode(SchemaTransformation$1.toLowerCase())).check(Schema$1.isMinLength(1).annotate({ expected: "a non-empty world" }));
|
|
305
|
+
Schema$1.String.check(Schema$1.isMaxLength(100)).pipe(Schema$1.decode(SchemaTransformation$1.trim()));
|
|
306
|
+
Schema$1.String.check(Schema$1.isMinLength(1), Schema$1.isMaxLength(512));
|
|
276
307
|
//#endregion
|
|
277
308
|
//#region ../protocol/src/loot-summary.ts
|
|
309
|
+
const MapPlayersSnapshot = Schema.Array(Schema.Struct({
|
|
310
|
+
accountId: PositiveSafeInteger,
|
|
311
|
+
characterId: PositiveSafeInteger,
|
|
312
|
+
name: NonEmptyString,
|
|
313
|
+
prof: Schema.NullOr(ProfessionSchema),
|
|
314
|
+
icon: Schema.NullOr(Schema.String)
|
|
315
|
+
})).check(Schema.isMinLength(1));
|
|
278
316
|
const LootItemResponse = Schema.Struct({
|
|
279
317
|
id: FiniteNumber,
|
|
280
318
|
hid: Schema.String,
|
|
@@ -314,6 +352,37 @@ const LootSummary = Schema.Struct({
|
|
|
314
352
|
lootShare: LootShareResponse,
|
|
315
353
|
location: Schema.String
|
|
316
354
|
});
|
|
355
|
+
/** Same shape as the HTTP `MemberSummary`. */
|
|
356
|
+
const LootMember = Schema.Struct({
|
|
357
|
+
id: Schema.Number,
|
|
358
|
+
name: Schema.String,
|
|
359
|
+
avatar: Schema.NullOr(Schema.String),
|
|
360
|
+
userId: Schema.String
|
|
361
|
+
});
|
|
362
|
+
const LootSubmission = Schema.Struct({
|
|
363
|
+
guildId: Schema.String,
|
|
364
|
+
memberId: Schema.Number,
|
|
365
|
+
lootId: Schema.Number,
|
|
366
|
+
member: LootMember
|
|
367
|
+
});
|
|
368
|
+
/** One Organization's view of a loot, as its HTTP list and detail responses encode it. */
|
|
369
|
+
const LootSnapshot = Schema.Struct({
|
|
370
|
+
id: Schema.Number,
|
|
371
|
+
uniqueId: Schema.String,
|
|
372
|
+
world: Schema.String,
|
|
373
|
+
source: LootSourceSchema,
|
|
374
|
+
location: Schema.String,
|
|
375
|
+
items: Schema.Array(LootItemResponse),
|
|
376
|
+
players: Schema.Array(LootPlayerResponse),
|
|
377
|
+
mapPlayersSnapshot: Schema.NullOr(MapPlayersSnapshot),
|
|
378
|
+
npcs: Schema.Array(LootNpcResponse),
|
|
379
|
+
lootShare: LootShareResponse,
|
|
380
|
+
createdAt: Schema.String,
|
|
381
|
+
updatedAt: Schema.String,
|
|
382
|
+
submissions: Schema.optionalKey(Schema.Array(LootSubmission)),
|
|
383
|
+
commentsCount: Schema.Number,
|
|
384
|
+
likes: Schema.optionalKey(Schema.Array(LootMember))
|
|
385
|
+
});
|
|
317
386
|
//#endregion
|
|
318
387
|
//#region ../protocol/src/feed.ts
|
|
319
388
|
const FeedNpc = Schema.Struct({
|
|
@@ -360,9 +429,204 @@ const UserFeedItem = Schema.Union([Schema.Struct({
|
|
|
360
429
|
})]);
|
|
361
430
|
//#endregion
|
|
362
431
|
//#region ../schema/src/primitives.ts
|
|
363
|
-
const NonEmptyString = Schema.NonEmptyString;
|
|
364
432
|
const NonNegativeInt = Schema.Int.check(Schema.isGreaterThanOrEqualTo(0));
|
|
365
|
-
|
|
433
|
+
const isoDateTimePattern = new RegExp(DATE_TIME_STRING_PATTERN, "u");
|
|
434
|
+
Schema.String.annotate({ format: "date-time" }).pipe(Schema.check(Schema.isPattern(isoDateTimePattern)), Schema.decodeTo(Schema.Date, SchemaTransformation.dateFromString));
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region ../schema/src/party-ready-room.ts
|
|
437
|
+
const PARTY_READY_ROOM_STATUSES = ["ACTIVE", "CANCELLED"];
|
|
438
|
+
const PARTY_READY_ROOM_PARTY_PRESENCE_STATES = ["OUTSIDE", "IN_PARTY"];
|
|
439
|
+
const PartyReadyRoomClanSchema = Schema.Struct({
|
|
440
|
+
id: Schema.optionalKey(Schema.Number),
|
|
441
|
+
name: Schema.optionalKey(Schema.String)
|
|
442
|
+
});
|
|
443
|
+
const PartyReadyRoomCharacterSchema = Schema.Struct({
|
|
444
|
+
accountId: Schema.String,
|
|
445
|
+
characterId: Schema.String,
|
|
446
|
+
icon: Schema.String,
|
|
447
|
+
lvl: Schema.Number,
|
|
448
|
+
nick: Schema.String,
|
|
449
|
+
prof: Schema.String,
|
|
450
|
+
clan: Schema.optionalKey(PartyReadyRoomClanSchema)
|
|
451
|
+
});
|
|
452
|
+
const PartyReadyRoomParticipantSchema = Schema.Struct({
|
|
453
|
+
participantId: Schema.String,
|
|
454
|
+
discordId: Schema.String,
|
|
455
|
+
character: PartyReadyRoomCharacterSchema,
|
|
456
|
+
partyPresence: Schema.Literals(PARTY_READY_ROOM_PARTY_PRESENCE_STATES),
|
|
457
|
+
createdAt: Schema.String,
|
|
458
|
+
updatedAt: Schema.String
|
|
459
|
+
});
|
|
460
|
+
const PartyGatheringNpcSchema = Schema.Struct({
|
|
461
|
+
prof: Schema.optionalKey(Schema.String),
|
|
462
|
+
icon: Schema.optionalKey(Schema.String),
|
|
463
|
+
name: Schema.String,
|
|
464
|
+
location: Schema.String,
|
|
465
|
+
lvl: Schema.Number,
|
|
466
|
+
type: Schema.String,
|
|
467
|
+
x: Schema.optionalKey(Schema.Number),
|
|
468
|
+
y: Schema.optionalKey(Schema.Number)
|
|
469
|
+
});
|
|
470
|
+
const PartyGatheringCharacterSchema = Schema.Struct({
|
|
471
|
+
characterId: NonEmptyString.check(Schema.isMaxLength(255)),
|
|
472
|
+
nick: Schema.String.check(Schema.isMaxLength(255)),
|
|
473
|
+
icon: Schema.String.check(Schema.isMaxLength(2048)),
|
|
474
|
+
lvl: FiniteNumber,
|
|
475
|
+
prof: Schema.String.check(Schema.isMaxLength(100))
|
|
476
|
+
});
|
|
477
|
+
const PartyGatheringPartyMemberSchema = Schema.Struct({
|
|
478
|
+
characterId: PartyGatheringCharacterSchema.fields.characterId,
|
|
479
|
+
nick: Schema.optionalKey(PartyGatheringCharacterSchema.fields.nick),
|
|
480
|
+
icon: Schema.optionalKey(PartyGatheringCharacterSchema.fields.icon),
|
|
481
|
+
lvl: Schema.optionalKey(PartyGatheringCharacterSchema.fields.lvl),
|
|
482
|
+
prof: Schema.optionalKey(PartyGatheringCharacterSchema.fields.prof)
|
|
483
|
+
});
|
|
484
|
+
const PartyGatheringPartyStateSchema = Schema.Union([Schema.Struct({ status: Schema.Literal("UNKNOWN") }), Schema.Struct({
|
|
485
|
+
status: Schema.Literal("OBSERVED"),
|
|
486
|
+
observedAt: DateTimeString,
|
|
487
|
+
members: Schema.Array(PartyGatheringPartyMemberSchema).check(Schema.isMaxLength(20))
|
|
488
|
+
})]);
|
|
489
|
+
const PartyGatheringVolunteerSchema = Schema.Struct({
|
|
490
|
+
...PartyGatheringCharacterSchema.fields,
|
|
491
|
+
partyPresence: Schema.Literals(PARTY_READY_ROOM_PARTY_PRESENCE_STATES)
|
|
492
|
+
});
|
|
493
|
+
const PartyGatheringSummarySchema = Schema.Struct({
|
|
494
|
+
notificationId: Schema.String,
|
|
495
|
+
organizerName: Schema.String,
|
|
496
|
+
organizerDiscordId: Schema.optionalKey(Schema.String),
|
|
497
|
+
organizerLvl: Schema.optionalKey(FiniteNumber),
|
|
498
|
+
organizerProf: Schema.optionalKey(Schema.String),
|
|
499
|
+
applicantCount: NonNegativeSafeInteger,
|
|
500
|
+
inPartyCount: NonNegativeSafeInteger,
|
|
501
|
+
partyMemberCount: Schema.optionalKey(NonNegativeSafeInteger),
|
|
502
|
+
revision: Schema.optionalKey(PositiveSafeInteger),
|
|
503
|
+
volunteers: Schema.optionalKey(Schema.Array(PartyGatheringVolunteerSchema)),
|
|
504
|
+
partyState: Schema.optionalKey(PartyGatheringPartyStateSchema),
|
|
505
|
+
guildIds: Schema.Array(Schema.String),
|
|
506
|
+
world: Schema.String,
|
|
507
|
+
description: Schema.optionalKey(Schema.String),
|
|
508
|
+
minLvl: Schema.optionalKey(FiniteNumber),
|
|
509
|
+
maxLvl: Schema.optionalKey(FiniteNumber),
|
|
510
|
+
npc: Schema.optionalKey(Schema.Struct({
|
|
511
|
+
...PartyGatheringNpcSchema.fields,
|
|
512
|
+
type: Schema.optionalKey(Schema.String)
|
|
513
|
+
})),
|
|
514
|
+
createdAt: DateTimeString,
|
|
515
|
+
expiresAt: DateTimeString
|
|
516
|
+
});
|
|
517
|
+
const PartyGatheringClientUpdateSchema = Schema.Union([Schema.Struct({
|
|
518
|
+
type: Schema.Literal("UPSERT"),
|
|
519
|
+
gathering: PartyGatheringSummarySchema
|
|
520
|
+
}), Schema.Struct({
|
|
521
|
+
type: Schema.Literal("REMOVE"),
|
|
522
|
+
notificationId: Schema.String,
|
|
523
|
+
revision: PositiveSafeInteger
|
|
524
|
+
})]);
|
|
525
|
+
Schema.Struct({
|
|
526
|
+
guildId: Schema.String,
|
|
527
|
+
guildIds: Schema.Array(Schema.String),
|
|
528
|
+
organizerDiscordId: Schema.String,
|
|
529
|
+
npc: Schema.optionalKey(PartyGatheringNpcSchema),
|
|
530
|
+
world: Schema.String,
|
|
531
|
+
notificationId: Schema.String,
|
|
532
|
+
revision: PositiveSafeInteger,
|
|
533
|
+
update: PartyGatheringClientUpdateSchema
|
|
534
|
+
});
|
|
535
|
+
const PartyReadyRoomAggregateSchema = Schema.Struct({
|
|
536
|
+
schemaVersion: Schema.Literal(3),
|
|
537
|
+
npc: Schema.optionalKey(PartyGatheringNpcSchema),
|
|
538
|
+
notificationId: Schema.String,
|
|
539
|
+
organizerDiscordId: Schema.String,
|
|
540
|
+
organizerCharacter: PartyReadyRoomCharacterSchema,
|
|
541
|
+
guildIds: Schema.mutable(Schema.Array(Schema.String)),
|
|
542
|
+
world: Schema.String,
|
|
543
|
+
description: Schema.optionalKey(Schema.String),
|
|
544
|
+
minLvl: Schema.optionalKey(Schema.Number),
|
|
545
|
+
maxLvl: Schema.optionalKey(Schema.Number),
|
|
546
|
+
partyMemberCount: Schema.optionalKey(NonNegativeSafeInteger),
|
|
547
|
+
partyState: Schema.optionalKey(PartyGatheringPartyStateSchema),
|
|
548
|
+
status: Schema.Literals(PARTY_READY_ROOM_STATUSES),
|
|
549
|
+
revision: Schema.Number,
|
|
550
|
+
createdAt: Schema.String,
|
|
551
|
+
updatedAt: Schema.String,
|
|
552
|
+
expiresAt: Schema.String,
|
|
553
|
+
participants: Schema.Record(Schema.String, PartyReadyRoomParticipantSchema)
|
|
554
|
+
});
|
|
555
|
+
const activeProjectionFields = {
|
|
556
|
+
volunteers: Schema.optionalKey(Schema.Array(PartyGatheringVolunteerSchema)),
|
|
557
|
+
...PartyReadyRoomAggregateSchema.fields,
|
|
558
|
+
participants: Schema.Record(Schema.String, Schema.mutableKey(PartyReadyRoomParticipantSchema)),
|
|
559
|
+
status: Schema.Literal("ACTIVE")
|
|
560
|
+
};
|
|
561
|
+
const PartyReadyRoomOrganizerProjectionSchema = Schema.Struct({
|
|
562
|
+
...activeProjectionFields,
|
|
563
|
+
viewer: Schema.Literal("ORGANIZER"),
|
|
564
|
+
ownedParticipantIds: Schema.mutable(Schema.Array(Schema.String))
|
|
565
|
+
});
|
|
566
|
+
const PartyReadyRoomParticipantProjectionSchema = Schema.Struct({
|
|
567
|
+
...activeProjectionFields,
|
|
568
|
+
viewer: Schema.Literal("PARTICIPANT")
|
|
569
|
+
});
|
|
570
|
+
const PartyReadyRoomProjectionSchema = Schema.Union([PartyReadyRoomOrganizerProjectionSchema, PartyReadyRoomParticipantProjectionSchema]);
|
|
571
|
+
Schema.decodeUnknownSync(PartyReadyRoomProjectionSchema);
|
|
572
|
+
const PartyReadyRoomUpsertUpdateSchema = Schema.Struct({
|
|
573
|
+
schemaVersion: Schema.Literal(3),
|
|
574
|
+
type: Schema.Literal("UPSERT"),
|
|
575
|
+
projection: PartyReadyRoomProjectionSchema
|
|
576
|
+
});
|
|
577
|
+
const PartyReadyRoomRemoveUpdateSchema = Schema.Struct({
|
|
578
|
+
schemaVersion: Schema.Literal(3),
|
|
579
|
+
type: Schema.Literal("REMOVE"),
|
|
580
|
+
notificationId: Schema.String,
|
|
581
|
+
revision: Schema.Number
|
|
582
|
+
});
|
|
583
|
+
const PartyReadyRoomClientUpdateSchema = Schema.Union([PartyReadyRoomUpsertUpdateSchema, PartyReadyRoomRemoveUpdateSchema]);
|
|
584
|
+
Schema.decodeUnknownSync(PartyReadyRoomClientUpdateSchema);
|
|
585
|
+
const GlobalChatMessageText = Schema.String.check(Schema.isTrimmed(), Schema.isMinLength(1), Schema.isMaxLength(128));
|
|
586
|
+
/**
|
|
587
|
+
* A Margonem world with its own global chat channel. Wherever a channel is
|
|
588
|
+
* named, an absent world means the channel every world shares.
|
|
589
|
+
*/
|
|
590
|
+
const GlobalChatWorld = Schema.NonEmptyString.check(Schema.isMaxLength(64));
|
|
591
|
+
/**
|
|
592
|
+
* A global chat message as every Member sees it. It crosses Organization
|
|
593
|
+
* boundaries, so it names the sender only by their Discord display name and
|
|
594
|
+
* never carries a User, Discord, Organization or character identifier.
|
|
595
|
+
*
|
|
596
|
+
* `isAdmin` and the worlds are optional only for messages published before
|
|
597
|
+
* channels existed; the API sets `isAdmin` on every message it stores.
|
|
598
|
+
*/
|
|
599
|
+
const GlobalChatMessageSchema = Schema.Struct({
|
|
600
|
+
id: Schema.NonEmptyString,
|
|
601
|
+
displayName: Schema.NonEmptyString,
|
|
602
|
+
message: GlobalChatMessageText,
|
|
603
|
+
timestamp: DateTimeString,
|
|
604
|
+
/** The channel's world; absent in the channel every world shares. */
|
|
605
|
+
world: Schema.optionalKey(GlobalChatWorld),
|
|
606
|
+
/** In the shared channel only: the world the sender wrote from. */
|
|
607
|
+
originWorld: Schema.optionalKey(GlobalChatWorld),
|
|
608
|
+
isAdmin: Schema.optionalKey(Schema.Boolean)
|
|
609
|
+
});
|
|
610
|
+
Schema.Union([Schema.Struct({
|
|
611
|
+
type: Schema.Literal("deleted"),
|
|
612
|
+
world: Schema.optionalKey(GlobalChatWorld),
|
|
613
|
+
id: Schema.NonEmptyString
|
|
614
|
+
}), Schema.Struct({
|
|
615
|
+
type: Schema.Literal("pinned"),
|
|
616
|
+
world: Schema.optionalKey(GlobalChatWorld),
|
|
617
|
+
message: Schema.NullOr(GlobalChatMessageSchema)
|
|
618
|
+
})]);
|
|
619
|
+
/** Live counts the gateway sends to a channel's subscribers. */
|
|
620
|
+
const GlobalChatStatsSchema = Schema.Struct({
|
|
621
|
+
world: Schema.optionalKey(GlobalChatWorld),
|
|
622
|
+
/**
|
|
623
|
+
* Unique Users with a live Game client across every Organization: on the
|
|
624
|
+
* channel's world, or on any world in the shared channel.
|
|
625
|
+
*/
|
|
626
|
+
online: NonNegativeInt,
|
|
627
|
+
/** Unique Users following this channel. */
|
|
628
|
+
listeners: NonNegativeInt
|
|
629
|
+
});
|
|
366
630
|
//#endregion
|
|
367
631
|
//#region ../schema/src/air-tag.ts
|
|
368
632
|
const AIR_TAG_RELATIONS = [
|
|
@@ -386,6 +650,11 @@ const Coordinate = Schema.Int.check(Schema.isBetween({
|
|
|
386
650
|
minimum: 0,
|
|
387
651
|
maximum: AIR_TAG_MAX_COORDINATE
|
|
388
652
|
}));
|
|
653
|
+
const Level = Schema.Int.check(Schema.isBetween({
|
|
654
|
+
minimum: 0,
|
|
655
|
+
maximum: 1e4
|
|
656
|
+
}));
|
|
657
|
+
const Profession = Schema.NonEmptyString.check(Schema.isMaxLength(1));
|
|
389
658
|
const AirTagRelationSchema = Schema.Literals(AIR_TAG_RELATIONS);
|
|
390
659
|
const AirTagClanSchema = Schema.Struct({
|
|
391
660
|
id: SafeNatural,
|
|
@@ -397,24 +666,22 @@ const AirTagObservationSchema = Schema.Struct({
|
|
|
397
666
|
clan: Schema.optionalKey(AirTagClanSchema),
|
|
398
667
|
relation: AirTagRelationSchema,
|
|
399
668
|
x: Coordinate,
|
|
400
|
-
y: Coordinate
|
|
669
|
+
y: Coordinate,
|
|
670
|
+
lvl: Schema.optionalKey(Level),
|
|
671
|
+
prof: Schema.optionalKey(Profession),
|
|
672
|
+
stasis: Schema.optionalKey(Schema.Boolean)
|
|
673
|
+
});
|
|
674
|
+
const AirTagDepartureSchema = Schema.Struct({
|
|
675
|
+
targetId: ShortString,
|
|
676
|
+
reason: Schema.Literals(["left-map", "out-of-sight"])
|
|
401
677
|
});
|
|
402
678
|
const AirTagObservationBatchSchema = Schema.Struct({
|
|
403
679
|
expectedMapId: Coordinate,
|
|
404
|
-
observations: Schema.Array(AirTagObservationSchema)
|
|
405
|
-
|
|
406
|
-
Schema.Struct({
|
|
407
|
-
requestId: ShortString,
|
|
408
|
-
enabled: Schema.Boolean,
|
|
409
|
-
expectedMapId: Schema.optionalKey(Coordinate)
|
|
680
|
+
observations: Schema.Array(AirTagObservationSchema),
|
|
681
|
+
departures: Schema.optionalKey(Schema.Array(AirTagDepartureSchema))
|
|
410
682
|
});
|
|
411
683
|
const AirTagTargetSchema = Schema.Struct({
|
|
412
|
-
|
|
413
|
-
nickname: ShortString,
|
|
414
|
-
clan: Schema.optionalKey(AirTagClanSchema),
|
|
415
|
-
relation: AirTagRelationSchema,
|
|
416
|
-
x: Coordinate,
|
|
417
|
-
y: Coordinate,
|
|
684
|
+
...AirTagObservationSchema.fields,
|
|
418
685
|
observedAt: SafeNatural,
|
|
419
686
|
enemyObservedAt: Schema.optionalKey(SafeNatural),
|
|
420
687
|
clanEnemyObservedAt: Schema.optionalKey(SafeNatural)
|
|
@@ -429,23 +696,220 @@ const AirTagScopeIdentityFields = {
|
|
|
429
696
|
};
|
|
430
697
|
const AirTagScopeSnapshotSchema = Schema.Struct({
|
|
431
698
|
...AirTagScopeIdentityFields,
|
|
432
|
-
targets: Schema.Array(AirTagTargetSchema)
|
|
699
|
+
targets: Schema.Array(AirTagTargetSchema),
|
|
700
|
+
serverTime: Schema.optionalKey(SafeNatural)
|
|
433
701
|
});
|
|
434
702
|
const AirTagUpdateEventSchema = Schema.Struct({
|
|
435
703
|
...AirTagScopeIdentityFields,
|
|
436
704
|
target: AirTagTargetSchema
|
|
437
705
|
});
|
|
438
|
-
Schema.
|
|
706
|
+
const AirTagScopeUpdateEventSchema = Schema.Struct({
|
|
707
|
+
...AirTagScopeIdentityFields,
|
|
708
|
+
targets: Schema.Array(AirTagTargetSchema),
|
|
709
|
+
removedTargetIds: Schema.Array(ShortString)
|
|
710
|
+
});
|
|
711
|
+
const AirTagMapThreatEventSchema = Schema.Struct({
|
|
712
|
+
guildId: GuildId,
|
|
713
|
+
world: ShortString,
|
|
714
|
+
mapId: Coordinate,
|
|
715
|
+
mapName: Schema.NonEmptyString.check(Schema.isMaxLength(128)),
|
|
716
|
+
revision: SafeNatural,
|
|
717
|
+
enemies: Schema.Array(Schema.Struct({
|
|
718
|
+
targetId: AirTagObservationSchema.fields.targetId,
|
|
719
|
+
nickname: AirTagObservationSchema.fields.nickname,
|
|
720
|
+
clan: AirTagObservationSchema.fields.clan,
|
|
721
|
+
lvl: AirTagObservationSchema.fields.lvl,
|
|
722
|
+
prof: AirTagObservationSchema.fields.prof,
|
|
723
|
+
stasis: AirTagObservationSchema.fields.stasis,
|
|
724
|
+
ageMs: SafeNatural
|
|
725
|
+
}))
|
|
726
|
+
});
|
|
439
727
|
Schema.is(AirTagObservationSchema);
|
|
440
|
-
Schema.is(
|
|
728
|
+
Schema.is(AirTagRelationSchema);
|
|
441
729
|
Schema.is(AirTagScopeSnapshotSchema);
|
|
442
730
|
Schema.is(AirTagUpdateEventSchema);
|
|
731
|
+
Schema.is(AirTagScopeUpdateEventSchema);
|
|
732
|
+
Schema.is(AirTagDepartureSchema);
|
|
733
|
+
//#endregion
|
|
734
|
+
//#region ../schema/src/battle-ping.ts
|
|
735
|
+
/** Pings placed on an enemy warrior. `attack` is the team's shared target. */
|
|
736
|
+
const BATTLE_ENEMY_PING_TYPES = [
|
|
737
|
+
"attack",
|
|
738
|
+
"caution",
|
|
739
|
+
"spare"
|
|
740
|
+
];
|
|
741
|
+
/** Requests placed on an allied warrior (or on the sender's own warrior). */
|
|
742
|
+
const BATTLE_REQUEST_PING_TYPES = [
|
|
743
|
+
"attack-speed-aura",
|
|
744
|
+
"atmo",
|
|
745
|
+
"cleanse",
|
|
746
|
+
"emanating-arrow",
|
|
747
|
+
"fury",
|
|
748
|
+
"heal",
|
|
749
|
+
"healing-wave",
|
|
750
|
+
"need-heal",
|
|
751
|
+
"poison",
|
|
752
|
+
"protection-aura",
|
|
753
|
+
"rime",
|
|
754
|
+
"stigma",
|
|
755
|
+
"taunt"
|
|
756
|
+
];
|
|
757
|
+
/**
|
|
758
|
+
* Calls to the whole battle team, sent from the sender's own warrior. Only
|
|
759
|
+
* sockets that negotiated `lootlog.battle-ping.team.v1` send or receive them.
|
|
760
|
+
*/
|
|
761
|
+
const BATTLE_TEAM_PING_TYPES = ["quick-fight"];
|
|
762
|
+
const BATTLE_PING_TYPES = [
|
|
763
|
+
...BATTLE_ENEMY_PING_TYPES,
|
|
764
|
+
...BATTLE_REQUEST_PING_TYPES,
|
|
765
|
+
...BATTLE_TEAM_PING_TYPES
|
|
766
|
+
];
|
|
767
|
+
/** A Margonem party holds at most ten characters, so nine other recipients. */
|
|
768
|
+
const MAX_BATTLE_PING_RECIPIENTS = 9;
|
|
769
|
+
/** Margonem battle warrior ids: positive for characters, negative for NPCs. */
|
|
770
|
+
const MAX_BATTLE_WARRIOR_ID = 2147483647;
|
|
771
|
+
const BattlePingTypeSchema = Schema.Literals(BATTLE_PING_TYPES);
|
|
772
|
+
const BattleWarriorIdSchema = Schema.Int.check(Schema.isBetween({
|
|
773
|
+
minimum: -2147483647,
|
|
774
|
+
maximum: MAX_BATTLE_WARRIOR_ID
|
|
775
|
+
}), Schema.makeFilter((id) => id !== 0 || "warrior id must not be 0"));
|
|
776
|
+
const CharacterIdSchema$1 = Schema.String.check(Schema.isPattern(/^[1-9]\d{0,9}$/u));
|
|
777
|
+
Schema.is(BattlePingTypeSchema);
|
|
778
|
+
Schema.is(Schema.Literals(BATTLE_ENEMY_PING_TYPES));
|
|
779
|
+
Schema.is(Schema.Literals(BATTLE_TEAM_PING_TYPES));
|
|
780
|
+
const BattlePingSendPayloadSchema = Schema.Struct({
|
|
781
|
+
expectedMapId: NonNegativeInt,
|
|
782
|
+
type: BattlePingTypeSchema,
|
|
783
|
+
warriorId: BattleWarriorIdSchema,
|
|
784
|
+
recipientCharacterIds: Schema.Array(CharacterIdSchema$1).check(Schema.isMinLength(1), Schema.isMaxLength(MAX_BATTLE_PING_RECIPIENTS))
|
|
785
|
+
});
|
|
786
|
+
const BattlePingEventSchema = Schema.Struct({
|
|
787
|
+
pingId: Schema.NonEmptyString,
|
|
788
|
+
world: Schema.NonEmptyString,
|
|
789
|
+
mapId: NonNegativeInt,
|
|
790
|
+
type: BattlePingTypeSchema,
|
|
791
|
+
warriorId: BattleWarriorIdSchema,
|
|
792
|
+
sender: Schema.Struct({
|
|
793
|
+
characterId: Schema.NonEmptyString,
|
|
794
|
+
name: Schema.NonEmptyString
|
|
795
|
+
}),
|
|
796
|
+
createdAt: NonNegativeInt
|
|
797
|
+
});
|
|
798
|
+
//#endregion
|
|
799
|
+
//#region ../schema/src/loot-split.ts
|
|
800
|
+
/**
|
|
801
|
+
* A participant's choice for one item of a group loot split, as Margonem's
|
|
802
|
+
* `loot.states` reports it: 0 "nie chcę", 1 "chcę", 2 "bardzo chcę".
|
|
803
|
+
*/
|
|
804
|
+
const LOOT_SPLIT_CHOICES = [
|
|
805
|
+
0,
|
|
806
|
+
1,
|
|
807
|
+
2
|
|
808
|
+
];
|
|
809
|
+
/** A Margonem party holds at most ten characters, so nine other recipients. */
|
|
810
|
+
const MAX_LOOT_SPLIT_RECIPIENTS = 9;
|
|
811
|
+
/** Bounds a frame; one group loot holds far fewer items. */
|
|
812
|
+
const MAX_LOOT_SPLIT_ITEMS = 64;
|
|
813
|
+
const ITEM_ID_PATTERN = /^[1-9]\d{0,17}$/u;
|
|
814
|
+
const ItemIdSchema = Schema.String.check(Schema.isPattern(ITEM_ID_PATTERN));
|
|
815
|
+
const CharacterIdSchema = Schema.String.check(Schema.isPattern(/^[1-9]\d{0,9}$/u));
|
|
816
|
+
const RecipientCharacterIdsSchema = Schema.Array(CharacterIdSchema).check(Schema.isMinLength(1), Schema.isMaxLength(MAX_LOOT_SPLIT_RECIPIENTS));
|
|
817
|
+
const SenderSchema = Schema.Struct({
|
|
818
|
+
characterId: Schema.NonEmptyString,
|
|
819
|
+
name: Schema.NonEmptyString
|
|
820
|
+
});
|
|
821
|
+
const LootSplitChoicesSchema = Schema.Record(Schema.String, Schema.Literals(LOOT_SPLIT_CHOICES)).check(Schema.makeFilter((choices) => {
|
|
822
|
+
const ids = Object.keys(choices);
|
|
823
|
+
return ids.length <= MAX_LOOT_SPLIT_ITEMS && ids.every((id) => ITEM_ID_PATTERN.test(id)) || "choices must map at most 64 item ids";
|
|
824
|
+
}));
|
|
825
|
+
const LootSplitSendPayloadSchema = Schema.Struct({
|
|
826
|
+
splitId: ItemIdSchema,
|
|
827
|
+
choices: LootSplitChoicesSchema,
|
|
828
|
+
accepted: Schema.Boolean,
|
|
829
|
+
recipientCharacterIds: RecipientCharacterIdsSchema
|
|
830
|
+
});
|
|
831
|
+
const LootSplitEventSchema = Schema.Struct({
|
|
832
|
+
splitId: ItemIdSchema,
|
|
833
|
+
world: Schema.NonEmptyString,
|
|
834
|
+
sender: SenderSchema,
|
|
835
|
+
choices: LootSplitChoicesSchema,
|
|
836
|
+
accepted: Schema.Boolean,
|
|
837
|
+
sentAt: NonNegativeInt
|
|
838
|
+
});
|
|
839
|
+
const LootSplitAutoAcceptPayloadSchema = Schema.Struct({ recipientCharacterIds: RecipientCharacterIdsSchema });
|
|
840
|
+
const LootSplitAutoAcceptedEventSchema = Schema.Struct({
|
|
841
|
+
world: Schema.NonEmptyString,
|
|
842
|
+
sender: SenderSchema,
|
|
843
|
+
sentAt: NonNegativeInt
|
|
844
|
+
});
|
|
845
|
+
const LootSplitAckSchema = Schema.Union([Schema.Struct({ status: Schema.Literal("accepted") }), Schema.Struct({
|
|
846
|
+
status: Schema.Literal("rejected"),
|
|
847
|
+
code: Schema.Literals([
|
|
848
|
+
"forbidden",
|
|
849
|
+
"invalid-context",
|
|
850
|
+
"invalid-payload",
|
|
851
|
+
"rate-limited",
|
|
852
|
+
"temporarily-unavailable"
|
|
853
|
+
]),
|
|
854
|
+
retryAfterMs: Schema.optionalKey(NonNegativeInt)
|
|
855
|
+
})]);
|
|
856
|
+
/** The fields organization NPC visibility needs, keyed by the NPC's runtime id. */
|
|
857
|
+
const NpcPresenceNpcSchema = Schema.Struct({
|
|
858
|
+
id: NonNegativeInt,
|
|
859
|
+
lvl: NonNegativeInt,
|
|
860
|
+
wt: NonNegativeInt,
|
|
861
|
+
prof: Schema.optionalKey(Schema.String.check(Schema.isMaxLength(8))),
|
|
862
|
+
type: Schema.optionalKey(NonNegativeInt)
|
|
863
|
+
});
|
|
864
|
+
/** Every NPC a character currently sees on its map; each report replaces the previous one. */
|
|
865
|
+
const NpcPresenceReportSchema = Schema.Struct({
|
|
866
|
+
world: Schema.NonEmptyString.check(Schema.isMaxLength(64)),
|
|
867
|
+
organizationIds: Schema.Array(Schema.NonEmptyString).check(Schema.isMaxLength(20)),
|
|
868
|
+
npcs: Schema.Array(NpcPresenceNpcSchema).check(Schema.isMaxLength(50))
|
|
869
|
+
});
|
|
870
|
+
/** Sent only when an NPC gains its first reporter or loses its last one. */
|
|
871
|
+
const NpcPresenceEventSchema = Schema.Struct({
|
|
872
|
+
organizationId: Schema.NonEmptyString,
|
|
873
|
+
world: Schema.NonEmptyString,
|
|
874
|
+
/** Orders the updates of one Organization and world across gateway replicas. */
|
|
875
|
+
revision: NonNegativeInt,
|
|
876
|
+
npc: NpcPresenceNpcSchema,
|
|
877
|
+
standing: Schema.Boolean,
|
|
878
|
+
/** Gateway time the NPC was first reported. */
|
|
879
|
+
since: NonNegativeInt
|
|
880
|
+
});
|
|
881
|
+
const NpcPresenceSnapshotSchema = Schema.Struct({
|
|
882
|
+
revision: NonNegativeInt,
|
|
883
|
+
npcs: Schema.Array(Schema.Struct({
|
|
884
|
+
npc: NpcPresenceNpcSchema,
|
|
885
|
+
since: NonNegativeInt
|
|
886
|
+
}))
|
|
887
|
+
});
|
|
888
|
+
const NpcPresenceRejectCode = Schema.Literals([
|
|
889
|
+
"forbidden",
|
|
890
|
+
"invalid-context",
|
|
891
|
+
"invalid-payload",
|
|
892
|
+
"rate-limited",
|
|
893
|
+
"temporarily-unavailable"
|
|
894
|
+
]);
|
|
895
|
+
const NpcPresenceReportAckSchema = Schema.Union([Schema.Struct({ status: Schema.Literal("accepted") }), Schema.Struct({
|
|
896
|
+
status: Schema.Literal("rejected"),
|
|
897
|
+
code: NpcPresenceRejectCode,
|
|
898
|
+
retryAfterMs: Schema.optional(NonNegativeInt)
|
|
899
|
+
})]);
|
|
900
|
+
//#endregion
|
|
901
|
+
//#region ../schema/src/map-ping.ts
|
|
443
902
|
const MapPingTypeSchema = Schema.Literals([
|
|
444
903
|
"attention",
|
|
445
904
|
"enemy",
|
|
446
905
|
"regroup",
|
|
447
906
|
"avoid"
|
|
448
907
|
]);
|
|
908
|
+
/** Margonem NPC and character ids are positive 32-bit integers. */
|
|
909
|
+
const MapPingCharacterIdSchema = Schema.Int.check(Schema.isBetween({
|
|
910
|
+
minimum: 1,
|
|
911
|
+
maximum: 2147483647
|
|
912
|
+
}));
|
|
449
913
|
const MapPingRejectCodeSchema = Schema.Literals([
|
|
450
914
|
"forbidden",
|
|
451
915
|
"invalid-context",
|
|
@@ -454,11 +918,16 @@ const MapPingRejectCodeSchema = Schema.Literals([
|
|
|
454
918
|
"temporarily-unavailable"
|
|
455
919
|
]);
|
|
456
920
|
Schema.is(MapPingTypeSchema);
|
|
457
|
-
const
|
|
458
|
-
expectedMapId: NonNegativeInt,
|
|
921
|
+
const mapPingFields = {
|
|
459
922
|
type: MapPingTypeSchema,
|
|
460
923
|
x: NonNegativeInt,
|
|
461
|
-
y: NonNegativeInt
|
|
924
|
+
y: NonNegativeInt,
|
|
925
|
+
npcId: Schema.optionalKey(MapPingCharacterIdSchema),
|
|
926
|
+
playerId: Schema.optionalKey(MapPingCharacterIdSchema)
|
|
927
|
+
};
|
|
928
|
+
const MapPingSendPayloadSchema = Schema.Struct({
|
|
929
|
+
expectedMapId: NonNegativeInt,
|
|
930
|
+
...mapPingFields
|
|
462
931
|
});
|
|
463
932
|
const MapPingAckSchema = Schema.Union([Schema.Struct({
|
|
464
933
|
status: Schema.Literal("accepted"),
|
|
@@ -472,9 +941,7 @@ const MapPingEventSchema = Schema.Struct({
|
|
|
472
941
|
pingId: Schema.NonEmptyString,
|
|
473
942
|
world: Schema.NonEmptyString,
|
|
474
943
|
mapId: NonNegativeInt,
|
|
475
|
-
|
|
476
|
-
x: NonNegativeInt,
|
|
477
|
-
y: NonNegativeInt,
|
|
944
|
+
...mapPingFields,
|
|
478
945
|
sender: Schema.Struct({
|
|
479
946
|
characterId: Schema.NonEmptyString,
|
|
480
947
|
name: Schema.NonEmptyString
|
|
@@ -482,302 +949,24 @@ const MapPingEventSchema = Schema.Struct({
|
|
|
482
949
|
createdAt: NonNegativeInt
|
|
483
950
|
});
|
|
484
951
|
//#endregion
|
|
485
|
-
//#region ../
|
|
486
|
-
const discordPermissionFields = {
|
|
487
|
-
hasRequiredPermissions: Schema.Boolean,
|
|
488
|
-
requiredPermissions: Schema.Array(Schema.String),
|
|
489
|
-
grantedPermissions: Schema.Array(Schema.String),
|
|
490
|
-
missingPermissions: Schema.Array(Schema.String)
|
|
491
|
-
};
|
|
492
|
-
const DiscordGuildSyncStatus = Schema.Literals([
|
|
493
|
-
"SYNCED",
|
|
494
|
-
"SYNCING",
|
|
495
|
-
"FAILED",
|
|
496
|
-
"STALE",
|
|
497
|
-
"NOT_FOUND"
|
|
498
|
-
]);
|
|
499
|
-
//#endregion
|
|
500
|
-
//#region ../protocol/src/rabbit/topology.ts
|
|
501
|
-
const RabbitExchange = {
|
|
502
|
-
DEFAULT: "default",
|
|
503
|
-
DEAD_LETTER: "dlx",
|
|
504
|
-
RETRY: "retry"
|
|
505
|
-
};
|
|
506
|
-
const RabbitExchangeName = Schema.Literals([
|
|
507
|
-
RabbitExchange.DEFAULT,
|
|
508
|
-
RabbitExchange.DEAD_LETTER,
|
|
509
|
-
RabbitExchange.RETRY
|
|
510
|
-
]);
|
|
511
|
-
const RabbitRoutingKey = {
|
|
512
|
-
USERS_ONLINE_CHECKPOINT_V1: "users.online.checkpoint.v1",
|
|
513
|
-
USERS_ONLINE_CHECKPOINT_V1_RETRY: "users.online.checkpoint.v1.retry",
|
|
514
|
-
USERS_ONLINE_CHECKPOINT_V1_DLQ: "users.online.checkpoint.v1.dlq",
|
|
515
|
-
ACTIVITY_LOG_CREATE: "activity.log.create",
|
|
516
|
-
ACTIVITY_LOG_CREATE_DLQ: "activity.log.create.dlq",
|
|
517
|
-
ACTIVITY_LOG_CREATE_RETRY: "activity.log.create.retry",
|
|
518
|
-
DISCORD_GUILD_CHANNEL_DELETED: "discord.guild.channel.deleted",
|
|
519
|
-
DISCORD_GUILD_CHANNEL_UPSERTED: "discord.guild.channel.upserted",
|
|
520
|
-
DISCORD_GUILD_CHANNELS_SYNC_FAILED: "discord.guild.channels.sync.failed",
|
|
521
|
-
DISCORD_GUILD_CHANNELS_SYNCED: "discord.guild.channels.synced",
|
|
522
|
-
DISCORD_GUILD_SYNC_STATE_UPDATED: "discord.guild.sync-state.updated",
|
|
523
|
-
EVENT_HERO_KILLED: "event.hero.killed",
|
|
524
|
-
EVENT_MAP_STATUS_UPDATE: "event.map-status.update",
|
|
525
|
-
EVENT_RANKING_UPDATE: "event.ranking.update",
|
|
526
|
-
EVENT_RESPAWN_WINDOW_CLOSED: "event.respawn-window.closed",
|
|
527
|
-
EVENT_RESPAWN_WINDOW_OPENED: "event.respawn-window.opened",
|
|
528
|
-
GUILDS_CLEAR_MESSAGES: "guilds.clear.messages",
|
|
529
|
-
GUILDS_CREATE: "guilds.create",
|
|
530
|
-
GUILDS_CREATE_DLQ: "guilds.create.dlq",
|
|
531
|
-
GUILDS_CREATE_RETRY: "guilds.create.retry",
|
|
532
|
-
GUILDS_CREATE_ROLE: "guilds.create.role",
|
|
533
|
-
GUILDS_CREATE_ROLE_DLQ: "guilds.create.role.dlq",
|
|
534
|
-
GUILDS_CREATE_ROLE_RETRY: "guilds.create.role.retry",
|
|
535
|
-
GUILDS_DELETE: "guilds.delete",
|
|
536
|
-
GUILDS_DELETE_DLQ: "guilds.delete.dlq",
|
|
537
|
-
GUILDS_DELETE_MESSAGE: "guilds.delete.message",
|
|
538
|
-
GUILDS_DELETE_RETRY: "guilds.delete.retry",
|
|
539
|
-
GUILDS_DELETE_ROLE: "guilds.delete.role",
|
|
540
|
-
GUILDS_DELETE_ROLE_DLQ: "guilds.delete.role.dlq",
|
|
541
|
-
GUILDS_DELETE_ROLE_RETRY: "guilds.delete.role.retry",
|
|
542
|
-
GUILDS_INITIALIZE: "guilds.initialize",
|
|
543
|
-
GUILDS_INITIALIZE_BOT: "guilds.initialize-bot",
|
|
544
|
-
GUILDS_INITIALIZE_DLQ: "guilds.initialize.dlq",
|
|
545
|
-
GUILDS_INITIALIZE_RETRY: "guilds.initialize.retry",
|
|
546
|
-
GUILDS_KILLS_ACCEPTED_V1: "guilds.kills.accepted.v1",
|
|
547
|
-
GUILDS_KILLS_ACCEPTED_V1_RETRY: "guilds.kills.accepted.v1.retry",
|
|
548
|
-
GUILDS_KILLS_ACCEPTED_V1_DLQ: "guilds.kills.accepted.v1.dlq",
|
|
549
|
-
GUILDS_LOOTS_CREATE: "guilds.loots.create",
|
|
550
|
-
GUILDS_LOOTS_CREATE_DLQ: "guilds.loots.create.dlq",
|
|
551
|
-
GUILDS_LOOTS_CREATE_RETRY: "guilds.loots.create.retry",
|
|
552
|
-
GUILDS_LOOTS_DELETE: "guilds.loots.delete",
|
|
553
|
-
GUILDS_LOOTS_SHARE_UPDATE: "guilds.loots.share.update",
|
|
554
|
-
GUILDS_LOOTS_SHARE_UPDATE_DLQ: "guilds.loots.share.update.dlq",
|
|
555
|
-
GUILDS_LOOTS_SHARE_UPDATE_RETRY: "guilds.loots.share.update.retry",
|
|
556
|
-
GUILDS_LOOTS_UPDATE: "guilds.loots.update",
|
|
557
|
-
GUILDS_MEMBERS_ADD: "guilds.members.add",
|
|
558
|
-
GUILDS_MEMBERS_ADD_DLQ: "guilds.members.add.dlq",
|
|
559
|
-
GUILDS_MEMBERS_ADD_RETRY: "guilds.members.add.retry",
|
|
560
|
-
GUILDS_MEMBERS_ADD_ROLE: "guilds.members.add.role",
|
|
561
|
-
GUILDS_MEMBERS_ADD_ROLE_DLQ: "guilds.members.add.role.dlq",
|
|
562
|
-
GUILDS_MEMBERS_ADD_ROLE_RETRY: "guilds.members.add.role.retry",
|
|
563
|
-
GUILDS_MEMBERS_REFRESH_JOB_UPDATE: "guilds.members.refresh.job.update",
|
|
564
|
-
GUILDS_MEMBERS_REMOVE: "guilds.members.remove",
|
|
565
|
-
GUILDS_MEMBERS_REMOVE_DLQ: "guilds.members.remove.dlq",
|
|
566
|
-
GUILDS_MEMBERS_REMOVE_RETRY: "guilds.members.remove.retry",
|
|
567
|
-
GUILDS_MEMBERS_REMOVE_ROLE: "guilds.members.remove.role",
|
|
568
|
-
GUILDS_MEMBERS_REMOVE_ROLE_DLQ: "guilds.members.remove.role.dlq",
|
|
569
|
-
GUILDS_MEMBERS_REMOVE_ROLE_RETRY: "guilds.members.remove.role.retry",
|
|
570
|
-
GUILDS_MEMBERS_UPDATE: "guilds.members.update",
|
|
571
|
-
GUILDS_MEMBERS_UPDATE_DLQ: "guilds.members.update.dlq",
|
|
572
|
-
GUILDS_MEMBERS_UPDATE_RETRY: "guilds.members.update.retry",
|
|
573
|
-
GUILDS_NOTIFICATIONS_SEND: "guilds.notifications.send",
|
|
574
|
-
GUILDS_NOTIFICATIONS_SEND_DLQ: "guilds.notifications.send.dlq",
|
|
575
|
-
GUILDS_NOTIFICATIONS_SEND_RETRY: "guilds.notifications.send.retry",
|
|
576
|
-
GUILDS_NOTIFICATIONS_VOLUNTEER: "guilds.notifications.volunteer",
|
|
577
|
-
GUILDS_NOTIFICATIONS_VOLUNTEER_DLQ: "guilds.notifications.volunteer.dlq",
|
|
578
|
-
GUILDS_NOTIFICATIONS_VOLUNTEER_RETRY: "guilds.notifications.volunteer.retry",
|
|
579
|
-
GUILDS_PARTY_GATHERING: "guilds.party-gathering",
|
|
580
|
-
GUILDS_PARTY_GATHERING_CANCEL: "guilds.party-gathering.cancel",
|
|
581
|
-
GUILDS_PARTY_GATHERING_DLQ: "guilds.party-gathering.dlq",
|
|
582
|
-
GUILDS_PARTY_GATHERING_RETRY: "guilds.party-gathering.retry",
|
|
583
|
-
GUILDS_RESERVATIONS_CHANGED_V2: "guilds.reservations.v2.changed",
|
|
584
|
-
GUILDS_RESERVATIONS_CHANGED_V2_DLQ: "guilds.reservations.v2.changed.dlq",
|
|
585
|
-
GUILDS_RESERVATIONS_CHANGED_V2_RETRY: "guilds.reservations.v2.changed.retry",
|
|
586
|
-
GUILDS_RESERVATIONS_CREATE: "guilds.reservations.create",
|
|
587
|
-
GUILDS_RESERVATIONS_CREATE_DLQ: "guilds.reservations.create.dlq",
|
|
588
|
-
GUILDS_RESERVATIONS_CREATE_RETRY: "guilds.reservations.create.retry",
|
|
589
|
-
GUILDS_RESERVATIONS_DELETE: "guilds.reservations.delete",
|
|
590
|
-
GUILDS_RESERVATIONS_DELETE_DLQ: "guilds.reservations.delete.dlq",
|
|
591
|
-
GUILDS_RESERVATIONS_DELETE_RETRY: "guilds.reservations.delete.retry",
|
|
592
|
-
GUILDS_SEND_MESSAGE: "guilds.send.message",
|
|
593
|
-
GUILDS_SEND_MESSAGE_DLQ: "guilds.send.message.dlq",
|
|
594
|
-
GUILDS_SEND_MESSAGE_RETRY: "guilds.send.message.retry",
|
|
595
|
-
GUILDS_SYNC: "guilds.sync",
|
|
596
|
-
GUILDS_SYNC_TRIGGER: "guilds.sync.trigger",
|
|
597
|
-
GUILDS_TIMERS_DELETE: "guilds.timers.delete",
|
|
598
|
-
GUILDS_TIMERS_DELETE_DLQ: "guilds.timers.delete.dlq",
|
|
599
|
-
GUILDS_TIMERS_DELETE_RETRY: "guilds.timers.delete.retry",
|
|
600
|
-
GUILDS_TIMERS_UPDATE: "guilds.timers.update",
|
|
601
|
-
GUILDS_TIMERS_UPDATE_DLQ: "guilds.timers.update.dlq",
|
|
602
|
-
GUILDS_TIMERS_UPDATE_RETRY: "guilds.timers.update.retry",
|
|
603
|
-
GUILDS_UPDATE: "guilds.update",
|
|
604
|
-
GUILDS_UPDATE_DLQ: "guilds.update.dlq",
|
|
605
|
-
GUILDS_UPDATE_MESSAGE: "guilds.update.message",
|
|
606
|
-
GUILDS_UPDATE_RETRY: "guilds.update.retry",
|
|
607
|
-
GUILDS_UPDATE_ROLE: "guilds.update.role",
|
|
608
|
-
GUILDS_UPDATE_ROLE_DLQ: "guilds.update.role.dlq",
|
|
609
|
-
GUILDS_UPDATE_ROLE_RETRY: "guilds.update.role.retry",
|
|
610
|
-
NOTIFICATIONS_DELIVERY_RESULT: "notifications.delivery.result",
|
|
611
|
-
NOTIFICATIONS_DISCORD_SEND: "notifications.discord.send",
|
|
612
|
-
NOTIFICATIONS_LOOT_CREATED: "notifications.loot.created",
|
|
613
|
-
NOTIFICATIONS_TIMER_DELETED: "notifications.timer.deleted",
|
|
614
|
-
NOTIFICATIONS_TIMER_UPDATED: "notifications.timer.updated",
|
|
615
|
-
GAME_CHARACTER_OFFLINE: "game.character.offline",
|
|
616
|
-
PRESENCE_CHECK_REQUEST: "presence.check.request",
|
|
617
|
-
PRESENCE_COVERAGE_CHECK: "presence.coverage.check",
|
|
618
|
-
SEARCH_ITEMS_INDEX: "search.items.index",
|
|
619
|
-
SEARCH_NPCS_INDEX: "search.npcs.index",
|
|
620
|
-
SEARCH_PLAYERS_INDEX: "search.players.index",
|
|
621
|
-
USERS_PARTY_READY_ROOM_UPDATED: "users.party-ready-room.updated",
|
|
622
|
-
USERS_PARTY_READY_ROOM_UPDATED_DLQ: "users.party-ready-room.updated.dlq",
|
|
623
|
-
USERS_PARTY_READY_ROOM_UPDATED_RETRY: "users.party-ready-room.updated.retry"
|
|
624
|
-
};
|
|
625
|
-
const RabbitRoutingKeyName = Schema.Literals(Object.values(RabbitRoutingKey));
|
|
626
|
-
Schema.Struct({
|
|
627
|
-
name: RabbitExchangeName,
|
|
628
|
-
type: Schema.Literal("topic"),
|
|
629
|
-
durable: Schema.Boolean
|
|
630
|
-
});
|
|
631
|
-
Schema.Struct({
|
|
632
|
-
name: Schema.String,
|
|
633
|
-
exchange: RabbitExchangeName,
|
|
634
|
-
routingKey: RabbitRoutingKeyName,
|
|
635
|
-
durable: Schema.Boolean,
|
|
636
|
-
messageTtl: Schema.optional(Schema.Int),
|
|
637
|
-
singleActiveConsumer: Schema.optional(Schema.Boolean),
|
|
638
|
-
deadLetterExchange: Schema.optional(RabbitExchangeName),
|
|
639
|
-
deadLetterRoutingKey: Schema.optional(RabbitRoutingKeyName)
|
|
640
|
-
});
|
|
641
|
-
RabbitExchange.DEFAULT, RabbitExchange.RETRY, RabbitExchange.DEAD_LETTER;
|
|
642
|
-
Schema.Struct({
|
|
643
|
-
userId: NonEmptyString,
|
|
644
|
-
discordId: NonEmptyString,
|
|
645
|
-
world: NonEmptyString,
|
|
646
|
-
characterId: NonEmptyString,
|
|
647
|
-
organizationIds: Schema.Array(NonEmptyString),
|
|
648
|
-
disconnectedAt: NonNegativeInt
|
|
649
|
-
});
|
|
650
|
-
const NullableString = Schema.NullOr(Schema.String);
|
|
651
|
-
const NullableNumber = Schema.NullOr(Schema.Number);
|
|
652
|
-
const GuildRole = Schema.Struct({
|
|
653
|
-
id: NonEmptyString,
|
|
654
|
-
name: NonEmptyString,
|
|
655
|
-
color: Schema.Number,
|
|
656
|
-
admin: Schema.Boolean,
|
|
657
|
-
position: Schema.Number
|
|
658
|
-
});
|
|
659
|
-
Schema.Struct({
|
|
660
|
-
guildId: NonEmptyString,
|
|
661
|
-
name: NonEmptyString,
|
|
662
|
-
icon: NullableString,
|
|
663
|
-
ownerId: NonEmptyString,
|
|
664
|
-
roles: Schema.Array(GuildRole)
|
|
665
|
-
});
|
|
666
|
-
Schema.Struct({
|
|
667
|
-
guildId: NonEmptyString,
|
|
668
|
-
name: NonEmptyString,
|
|
669
|
-
icon: NullableString,
|
|
670
|
-
ownerId: NonEmptyString
|
|
671
|
-
});
|
|
672
|
-
Schema.Struct({ guildId: NonEmptyString });
|
|
673
|
-
Schema.Struct({
|
|
674
|
-
guildId: NonEmptyString,
|
|
675
|
-
...GuildRole.fields
|
|
676
|
-
});
|
|
677
|
-
Schema.Struct({
|
|
678
|
-
guildId: NonEmptyString,
|
|
679
|
-
id: NonEmptyString
|
|
680
|
-
});
|
|
681
|
-
Schema.Struct({
|
|
682
|
-
guildId: NonEmptyString,
|
|
683
|
-
discordId: NonEmptyString,
|
|
684
|
-
userId: NonEmptyString
|
|
685
|
-
});
|
|
686
|
-
Schema.Struct({
|
|
687
|
-
guildId: NonEmptyString,
|
|
688
|
-
mapName: NonEmptyString
|
|
689
|
-
});
|
|
690
|
-
Schema.Struct({
|
|
691
|
-
guildId: NonEmptyString,
|
|
692
|
-
mapName: NonEmptyString,
|
|
693
|
-
discordId: NonEmptyString,
|
|
694
|
-
hasPlayer: Schema.Boolean,
|
|
695
|
-
isAfk: Schema.optional(Schema.Boolean)
|
|
696
|
-
});
|
|
697
|
-
const DiscordGuildChannel = Schema.Struct({
|
|
698
|
-
guildId: NonEmptyString,
|
|
699
|
-
channelId: NonEmptyString,
|
|
700
|
-
name: NonEmptyString,
|
|
701
|
-
channelType: NonEmptyString,
|
|
702
|
-
parentId: NullableString,
|
|
703
|
-
position: Schema.Number,
|
|
704
|
-
active: Schema.Boolean,
|
|
705
|
-
canView: Schema.Boolean,
|
|
706
|
-
canSend: Schema.Boolean,
|
|
707
|
-
...discordPermissionFields,
|
|
708
|
-
lastSyncedAt: NonEmptyString
|
|
709
|
-
});
|
|
710
|
-
const DiscordGuildSyncState = Schema.Struct({
|
|
711
|
-
guildId: NonEmptyString,
|
|
712
|
-
status: DiscordGuildSyncStatus,
|
|
713
|
-
...discordPermissionFields,
|
|
714
|
-
channelCount: NonNegativeInt,
|
|
715
|
-
selectableChannelCount: NonNegativeInt,
|
|
716
|
-
lastAttemptAt: NullableString,
|
|
717
|
-
lastSuccessAt: NullableString,
|
|
718
|
-
lastError: NullableString,
|
|
719
|
-
updatedAt: NonEmptyString
|
|
720
|
-
});
|
|
721
|
-
Schema.Struct({
|
|
722
|
-
guildId: NonEmptyString,
|
|
723
|
-
channels: Schema.Array(DiscordGuildChannel),
|
|
724
|
-
syncState: DiscordGuildSyncState
|
|
725
|
-
});
|
|
726
|
-
Schema.Struct({
|
|
727
|
-
guildId: NonEmptyString,
|
|
728
|
-
channel: DiscordGuildChannel,
|
|
729
|
-
syncState: DiscordGuildSyncState
|
|
730
|
-
});
|
|
731
|
-
Schema.Struct({
|
|
732
|
-
guildId: NonEmptyString,
|
|
733
|
-
status: DiscordGuildSyncStatus,
|
|
734
|
-
lastAttemptAt: NonEmptyString,
|
|
735
|
-
lastError: NonEmptyString
|
|
736
|
-
});
|
|
737
|
-
Schema.Struct({
|
|
738
|
-
guildId: NonEmptyString,
|
|
739
|
-
syncState: DiscordGuildSyncState
|
|
740
|
-
});
|
|
741
|
-
Schema.Union([Schema.Struct({ guildId: NonEmptyString }), Schema.Struct({ organizationId: NonEmptyString })]);
|
|
742
|
-
Schema.Struct({
|
|
743
|
-
recipientDiscordId: NonEmptyString,
|
|
744
|
-
eligibleGuildIds: Schema.Array(NonEmptyString),
|
|
745
|
-
update: Schema.Unknown
|
|
746
|
-
});
|
|
747
|
-
Schema.Struct({
|
|
748
|
-
notificationId: NonEmptyString,
|
|
749
|
-
targetDiscordId: NonEmptyString,
|
|
750
|
-
volunteerDiscordId: NonEmptyString,
|
|
751
|
-
world: NonEmptyString,
|
|
752
|
-
character: Schema.Unknown
|
|
753
|
-
});
|
|
952
|
+
//#region ../protocol/src/loot-events.ts
|
|
754
953
|
const GuildLootEventNpc = Schema.Struct({
|
|
755
|
-
lvl: Schema.optional(
|
|
756
|
-
prof: Schema.optional(
|
|
954
|
+
lvl: Schema.optional(Schema.NullOr(Schema.Number)),
|
|
955
|
+
prof: Schema.optional(Schema.NullOr(Schema.String)),
|
|
757
956
|
type: Schema.optional(Schema.NullOr(Schema.Union([Schema.Number, Schema.String]))),
|
|
758
957
|
wt: Schema.optional(Schema.NullOr(Schema.Union([Schema.Number, Schema.String])))
|
|
759
958
|
});
|
|
760
|
-
Schema.Struct({
|
|
761
|
-
sourceNpcs: Schema.optional(Schema.Array(Schema.Struct({
|
|
762
|
-
level: NonNegativeInt,
|
|
763
|
-
type: NpcTypeSchema
|
|
764
|
-
}))),
|
|
765
|
-
feedEntry: Schema.optional(UserFeedItem),
|
|
766
|
-
version: Schema.Literal(1),
|
|
767
|
-
guildId: NonEmptyString,
|
|
768
|
-
world: NonEmptyString,
|
|
769
|
-
npc: Schema.Struct({
|
|
770
|
-
type: NpcTypeSchema,
|
|
771
|
-
lvl: NonNegativeInt
|
|
772
|
-
})
|
|
773
|
-
});
|
|
774
959
|
const GuildLootCreatedEventV2 = Schema.Struct({
|
|
775
960
|
feedEntry: Schema.optional(UserFeedItem),
|
|
776
961
|
version: Schema.Literal(2),
|
|
777
|
-
guildId: NonEmptyString,
|
|
962
|
+
guildId: Schema.NonEmptyString,
|
|
778
963
|
lootId: NonNegativeInt,
|
|
779
964
|
npcs: Schema.Array(GuildLootEventNpc)
|
|
780
965
|
});
|
|
966
|
+
const GuildLootCreatedMessageV2 = Schema.Struct({
|
|
967
|
+
...GuildLootCreatedEventV2.fields,
|
|
968
|
+
loot: Schema.optional(LootSnapshot)
|
|
969
|
+
});
|
|
781
970
|
const GuildLootShareUpdatedEventV2 = Schema.Struct({
|
|
782
971
|
...GuildLootCreatedEventV2.fields,
|
|
783
972
|
lootShare: Schema.Record(Schema.String, Schema.Array(Schema.String))
|
|
@@ -790,105 +979,30 @@ const ReservationChangedEventV2 = Schema.Struct({
|
|
|
790
979
|
"deleted",
|
|
791
980
|
"sharing-changed"
|
|
792
981
|
]),
|
|
793
|
-
sourceGuildId: NonEmptyString,
|
|
794
|
-
audienceGuildIds: Schema.Array(NonEmptyString),
|
|
982
|
+
sourceGuildId: Schema.NonEmptyString,
|
|
983
|
+
audienceGuildIds: Schema.Array(Schema.NonEmptyString),
|
|
795
984
|
reservationId: Schema.NullOr(NonNegativeInt),
|
|
796
985
|
spotId: Schema.NullOr(Schema.String)
|
|
797
986
|
});
|
|
798
|
-
const
|
|
799
|
-
|
|
800
|
-
eventId: NonEmptyString
|
|
801
|
-
});
|
|
802
|
-
Schema.Struct({
|
|
803
|
-
...EventScope.fields,
|
|
804
|
-
heroNpcLvl: Schema.optional(Schema.NullOr(NonNegativeInt)),
|
|
805
|
-
mapId: NonEmptyString,
|
|
806
|
-
reason: Schema.optional(Schema.String)
|
|
807
|
-
});
|
|
808
|
-
Schema.Struct({
|
|
809
|
-
...EventScope.fields,
|
|
810
|
-
heroNpcLvl: Schema.optional(Schema.NullOr(NonNegativeInt)),
|
|
811
|
-
heroId: Schema.optional(NonEmptyString)
|
|
812
|
-
});
|
|
813
|
-
Schema.Struct({
|
|
814
|
-
...EventScope.fields,
|
|
815
|
-
heroNpcLvl: Schema.optional(Schema.NullOr(NonNegativeInt)),
|
|
816
|
-
heroId: NonEmptyString
|
|
817
|
-
});
|
|
818
|
-
Schema.Struct({
|
|
819
|
-
userId: NonEmptyString,
|
|
820
|
-
guildId: Schema.optional(NonEmptyString),
|
|
821
|
-
action: NonEmptyString,
|
|
822
|
-
entityType: Schema.optional(NonEmptyString),
|
|
823
|
-
entityId: Schema.optional(Schema.Union([Schema.String, Schema.Number])),
|
|
824
|
-
metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown))
|
|
825
|
-
});
|
|
826
|
-
Schema.Struct({
|
|
827
|
-
guildId: NonEmptyString,
|
|
828
|
-
world: NonEmptyString,
|
|
829
|
-
npcId: NonNegativeInt,
|
|
830
|
-
timerKey: NonEmptyString,
|
|
831
|
-
minSpawnTime: NonEmptyString,
|
|
832
|
-
maxSpawnTime: NonEmptyString,
|
|
833
|
-
npc: Schema.optional(Schema.NullOr(Schema.Struct({ name: Schema.optional(Schema.String) })))
|
|
834
|
-
});
|
|
835
|
-
Schema.Struct({
|
|
836
|
-
guildId: NonEmptyString,
|
|
837
|
-
world: NonEmptyString,
|
|
838
|
-
timerKey: NonEmptyString,
|
|
839
|
-
npcId: Schema.optional(NonNegativeInt)
|
|
840
|
-
});
|
|
841
|
-
Schema.Struct({
|
|
842
|
-
version: Schema.Literal(2),
|
|
843
|
-
lootId: NonNegativeInt,
|
|
844
|
-
world: NonEmptyString,
|
|
845
|
-
guildIds: Schema.Array(NonEmptyString),
|
|
846
|
-
itemIds: Schema.Array(NonNegativeInt),
|
|
847
|
-
itemNames: Schema.Array(Schema.String),
|
|
848
|
-
npcs: Schema.Array(Schema.Struct({
|
|
849
|
-
type: NullableString,
|
|
850
|
-
lvl: NullableNumber
|
|
851
|
-
}))
|
|
852
|
-
});
|
|
853
|
-
Schema.Struct({
|
|
854
|
-
notificationJobId: NonEmptyString,
|
|
855
|
-
success: Schema.Boolean,
|
|
856
|
-
retryable: Schema.Boolean,
|
|
857
|
-
providerMessageId: Schema.optional(NullableString),
|
|
858
|
-
errorCode: Schema.optional(NullableString),
|
|
859
|
-
errorMessage: Schema.optional(NullableString),
|
|
860
|
-
deliveredAt: NonEmptyString
|
|
861
|
-
});
|
|
862
|
-
Schema.Struct({
|
|
863
|
-
guildId: NonEmptyString,
|
|
864
|
-
channelId: NonEmptyString,
|
|
865
|
-
syncState: DiscordGuildSyncState
|
|
866
|
-
});
|
|
867
|
-
/** Cumulative confirmed game presence. A gap starts a new segment; retries never extend it. */
|
|
868
|
-
const UserOnlineCheckpointV1 = Schema.Struct({
|
|
869
|
-
version: Schema.Literal(1),
|
|
870
|
-
type: Schema.Literal("checkpoint"),
|
|
871
|
-
userId: NonEmptyString,
|
|
872
|
-
sessionId: NonEmptyString,
|
|
873
|
-
segmentId: NonEmptyString,
|
|
874
|
-
world: Schema.optional(NonEmptyString),
|
|
875
|
-
startedAt: DateTimeWithOffsetString,
|
|
876
|
-
endedAt: DateTimeWithOffsetString,
|
|
877
|
-
observedAt: DateTimeWithOffsetString
|
|
878
|
-
}).check(Schema.makeFilter((event) => Date.parse(event.startedAt) <= Date.parse(event.endedAt) && Date.parse(event.endedAt) <= Date.parse(event.observedAt), { expected: "ordered confirmed interval timestamps" }));
|
|
879
|
-
/** Published only after the durable publisher has drained its pending checkpoints. */
|
|
880
|
-
const UserOnlineHealthV1 = Schema.Struct({
|
|
881
|
-
version: Schema.Literal(1),
|
|
882
|
-
type: Schema.Literal("collector"),
|
|
883
|
-
observedAt: DateTimeWithOffsetString,
|
|
884
|
-
status: Schema.Literals(["healthy", "degraded"])
|
|
885
|
-
});
|
|
886
|
-
Schema.Union([UserOnlineCheckpointV1, UserOnlineHealthV1]);
|
|
887
|
-
RabbitRoutingKey.GAME_CHARACTER_OFFLINE, RabbitRoutingKey.GUILDS_KILLS_ACCEPTED_V1, RabbitRoutingKey.USERS_ONLINE_CHECKPOINT_V1, RabbitRoutingKey.ACTIVITY_LOG_CREATE, RabbitRoutingKey.DISCORD_GUILD_CHANNEL_DELETED, RabbitRoutingKey.DISCORD_GUILD_CHANNEL_UPSERTED, RabbitRoutingKey.DISCORD_GUILD_CHANNELS_SYNC_FAILED, RabbitRoutingKey.DISCORD_GUILD_CHANNELS_SYNCED, RabbitRoutingKey.DISCORD_GUILD_SYNC_STATE_UPDATED, RabbitRoutingKey.EVENT_HERO_KILLED, RabbitRoutingKey.EVENT_MAP_STATUS_UPDATE, RabbitRoutingKey.EVENT_RANKING_UPDATE, RabbitRoutingKey.EVENT_RESPAWN_WINDOW_CLOSED, RabbitRoutingKey.EVENT_RESPAWN_WINDOW_OPENED, RabbitRoutingKey.GUILDS_LOOTS_CREATE, RabbitRoutingKey.GUILDS_LOOTS_SHARE_UPDATE, RabbitRoutingKey.GUILDS_CREATE, RabbitRoutingKey.GUILDS_DELETE, RabbitRoutingKey.GUILDS_UPDATE, RabbitRoutingKey.GUILDS_CREATE_ROLE, RabbitRoutingKey.GUILDS_DELETE_ROLE, RabbitRoutingKey.GUILDS_UPDATE_ROLE, RabbitRoutingKey.GUILDS_MEMBERS_ADD, RabbitRoutingKey.GUILDS_MEMBERS_ADD_ROLE, RabbitRoutingKey.GUILDS_MEMBERS_REMOVE, RabbitRoutingKey.GUILDS_MEMBERS_REMOVE_ROLE, RabbitRoutingKey.GUILDS_MEMBERS_UPDATE, RabbitRoutingKey.GUILDS_TIMERS_UPDATE, RabbitRoutingKey.GUILDS_TIMERS_DELETE, RabbitRoutingKey.GUILDS_RESERVATIONS_CREATE, RabbitRoutingKey.GUILDS_RESERVATIONS_DELETE, RabbitRoutingKey.GUILDS_SEND_MESSAGE, RabbitRoutingKey.GUILDS_UPDATE_MESSAGE, RabbitRoutingKey.GUILDS_DELETE_MESSAGE, RabbitRoutingKey.GUILDS_CLEAR_MESSAGES, RabbitRoutingKey.GUILDS_NOTIFICATIONS_SEND, RabbitRoutingKey.GUILDS_NOTIFICATIONS_VOLUNTEER, RabbitRoutingKey.GUILDS_PARTY_GATHERING, RabbitRoutingKey.GUILDS_PARTY_GATHERING_CANCEL, RabbitRoutingKey.GUILDS_MEMBERS_REFRESH_JOB_UPDATE, RabbitRoutingKey.GUILDS_RESERVATIONS_CHANGED_V2, RabbitRoutingKey.NOTIFICATIONS_DELIVERY_RESULT, RabbitRoutingKey.NOTIFICATIONS_LOOT_CREATED, RabbitRoutingKey.NOTIFICATIONS_TIMER_DELETED, RabbitRoutingKey.NOTIFICATIONS_TIMER_UPDATED, RabbitRoutingKey.PRESENCE_CHECK_REQUEST, RabbitRoutingKey.PRESENCE_COVERAGE_CHECK, RabbitRoutingKey.USERS_PARTY_READY_ROOM_UPDATED;
|
|
987
|
+
const { feedEntry: _feedEntry, ...lootCreatedFields } = GuildLootCreatedMessageV2.fields;
|
|
988
|
+
const LootSnapshotEvent = Schema.Struct(lootCreatedFields);
|
|
888
989
|
const REALTIME_SUBPROTOCOL = "lootlog.realtime.v1";
|
|
889
990
|
const REALTIME_JSON_SUBPROTOCOL = "lootlog.realtime.json.v1";
|
|
991
|
+
const REALTIME_CLIENT_CLOSE_CODES = {
|
|
992
|
+
heartbeatTimeout: 4001,
|
|
993
|
+
heartbeatRejected: 4002,
|
|
994
|
+
heartbeatUnavailable: 4003,
|
|
995
|
+
malformedFrame: 4007,
|
|
996
|
+
sessionJoinFailed: 4008
|
|
997
|
+
};
|
|
998
|
+
const REALTIME_SERVER_CLOSE_CODES = {
|
|
999
|
+
restarting: 1012,
|
|
1000
|
+
tryAgainLater: 1013
|
|
1001
|
+
};
|
|
1002
|
+
const REALTIME_SERVER_CLOSE_RECONNECT_WINDOW_MS = 3e4;
|
|
890
1003
|
const PRESENCE_HEARTBEAT_INTERVAL_MS = 25e3;
|
|
891
|
-
const
|
|
1004
|
+
const PRESENCE_EXPIRY_MS = 6e4;
|
|
1005
|
+
const RequestId = Schema.NonEmptyString;
|
|
892
1006
|
const Revision = NonNegativeInt;
|
|
893
1007
|
const Timestamp = NonNegativeInt;
|
|
894
1008
|
const RealtimeLogicalTopic = Schema.Literals([
|
|
@@ -903,13 +1017,15 @@ const RealtimeLogicalTopic = Schema.Literals([
|
|
|
903
1017
|
"event.coordination",
|
|
904
1018
|
"map.air-tags",
|
|
905
1019
|
"map.pings",
|
|
906
|
-
"party.ready-room"
|
|
1020
|
+
"party.ready-room",
|
|
1021
|
+
"global.chat",
|
|
1022
|
+
"global.chat.world"
|
|
907
1023
|
]);
|
|
908
1024
|
const SubscriptionScope = Schema.Struct({
|
|
909
1025
|
topic: RealtimeLogicalTopic,
|
|
910
|
-
organizationId: Schema.optional(NonEmptyString),
|
|
911
|
-
eventId: Schema.optional(NonEmptyString),
|
|
912
|
-
world: Schema.optional(NonEmptyString),
|
|
1026
|
+
organizationId: Schema.optional(Schema.NonEmptyString),
|
|
1027
|
+
eventId: Schema.optional(Schema.NonEmptyString),
|
|
1028
|
+
world: Schema.optional(Schema.NonEmptyString),
|
|
913
1029
|
mapId: Schema.optional(NonNegativeInt)
|
|
914
1030
|
});
|
|
915
1031
|
const PresencePlatform = Schema.Literals(["game", "web-app"]);
|
|
@@ -921,26 +1037,26 @@ const PresenceClan = Schema.Struct({
|
|
|
921
1037
|
rank: Schema.optional(Schema.Int)
|
|
922
1038
|
});
|
|
923
1039
|
const PresenceCharacter = Schema.Struct({
|
|
924
|
-
world: NonEmptyString,
|
|
925
|
-
name: NonEmptyString,
|
|
1040
|
+
world: Schema.NonEmptyString,
|
|
1041
|
+
name: Schema.NonEmptyString,
|
|
926
1042
|
lvl: NonNegativeInt,
|
|
927
1043
|
icon: Schema.String,
|
|
928
|
-
characterId: NonEmptyString,
|
|
929
|
-
accountId: NonEmptyString,
|
|
1044
|
+
characterId: Schema.NonEmptyString,
|
|
1045
|
+
accountId: Schema.NonEmptyString,
|
|
930
1046
|
prof: Schema.String,
|
|
931
1047
|
clan: Schema.optional(PresenceClan)
|
|
932
1048
|
});
|
|
933
1049
|
const PrecisePresenceLocation = Schema.Struct({
|
|
934
1050
|
mapId: Schema.optional(NonNegativeInt),
|
|
935
|
-
map: NonEmptyString,
|
|
1051
|
+
map: Schema.NonEmptyString,
|
|
936
1052
|
x: Schema.optional(NonNegativeInt),
|
|
937
1053
|
y: Schema.optional(NonNegativeInt)
|
|
938
1054
|
});
|
|
939
1055
|
const BasicPresence = Schema.Struct({
|
|
940
|
-
userId: NonEmptyString,
|
|
941
|
-
discordId: Schema.optional(NonEmptyString),
|
|
942
|
-
sessionId: NonEmptyString,
|
|
943
|
-
organizationIds: Schema.Array(NonEmptyString),
|
|
1056
|
+
userId: Schema.NonEmptyString,
|
|
1057
|
+
discordId: Schema.optional(Schema.NonEmptyString),
|
|
1058
|
+
sessionId: Schema.NonEmptyString,
|
|
1059
|
+
organizationIds: Schema.Array(Schema.NonEmptyString),
|
|
944
1060
|
platform: PresencePlatform,
|
|
945
1061
|
status: PresenceStatus,
|
|
946
1062
|
confidence: PresenceConfidence,
|
|
@@ -953,29 +1069,29 @@ const PresenceWithLocation = Schema.Struct({
|
|
|
953
1069
|
location: PrecisePresenceLocation
|
|
954
1070
|
});
|
|
955
1071
|
const PublishedPresence = Schema.Struct({
|
|
956
|
-
organizationIds: Schema.Array(NonEmptyString),
|
|
1072
|
+
organizationIds: Schema.Array(Schema.NonEmptyString),
|
|
957
1073
|
isAfk: Schema.optional(Schema.Boolean),
|
|
958
1074
|
character: Schema.optional(PresenceCharacter),
|
|
959
1075
|
location: Schema.optional(PrecisePresenceLocation),
|
|
960
1076
|
clientObservedAt: Schema.optional(Timestamp)
|
|
961
1077
|
});
|
|
962
1078
|
const PresenceSnapshot = Schema.Struct({
|
|
963
|
-
organizationId: NonEmptyString,
|
|
964
|
-
world: Schema.optional(NonEmptyString),
|
|
1079
|
+
organizationId: Schema.NonEmptyString,
|
|
1080
|
+
world: Schema.optional(Schema.NonEmptyString),
|
|
965
1081
|
revision: Revision,
|
|
966
1082
|
presences: Schema.Array(Schema.Union([PresenceWithLocation, BasicPresence]))
|
|
967
1083
|
});
|
|
968
1084
|
const PresenceDelta = Schema.Struct({
|
|
969
|
-
organizationId: NonEmptyString,
|
|
1085
|
+
organizationId: Schema.NonEmptyString,
|
|
970
1086
|
revision: Revision,
|
|
971
1087
|
changes: Schema.Array(Schema.Union([Schema.Struct({
|
|
972
1088
|
action: Schema.Literal("upsert"),
|
|
973
1089
|
presence: Schema.Union([PresenceWithLocation, BasicPresence])
|
|
974
1090
|
}), Schema.Struct({
|
|
975
1091
|
action: Schema.Literal("remove"),
|
|
976
|
-
userId: NonEmptyString,
|
|
977
|
-
discordId: Schema.optional(NonEmptyString),
|
|
978
|
-
sessionId: NonEmptyString
|
|
1092
|
+
userId: Schema.NonEmptyString,
|
|
1093
|
+
discordId: Schema.optional(Schema.NonEmptyString),
|
|
1094
|
+
sessionId: Schema.NonEmptyString
|
|
979
1095
|
})]))
|
|
980
1096
|
});
|
|
981
1097
|
const command = (type, data) => Schema.Struct({
|
|
@@ -985,25 +1101,40 @@ const command = (type, data) => Schema.Struct({
|
|
|
985
1101
|
data
|
|
986
1102
|
});
|
|
987
1103
|
const SessionJoinCommand = command("session.join", Schema.Struct({
|
|
988
|
-
world: Schema.optional(NonEmptyString),
|
|
1104
|
+
world: Schema.optional(Schema.NonEmptyString),
|
|
989
1105
|
character: Schema.optional(PresenceCharacter),
|
|
990
1106
|
margonemAccountProof: Schema.optional(Schema.Unknown)
|
|
991
1107
|
}));
|
|
992
|
-
const HeartbeatCommand = command("presence.heartbeat", Schema.Struct({ sessionId: NonEmptyString }));
|
|
1108
|
+
const HeartbeatCommand = command("presence.heartbeat", Schema.Struct({ sessionId: Schema.NonEmptyString }));
|
|
993
1109
|
const PresencePublishCommand = command("presence.publish", PublishedPresence);
|
|
1110
|
+
const PingCommand = command("connection.ping", Schema.Struct({}));
|
|
994
1111
|
const PresenceFetchCommand = command("presence.fetch", Schema.Struct({
|
|
995
|
-
organizationId: NonEmptyString,
|
|
996
|
-
world: Schema.optional(NonEmptyString)
|
|
1112
|
+
organizationId: Schema.NonEmptyString,
|
|
1113
|
+
world: Schema.optional(Schema.NonEmptyString),
|
|
1114
|
+
delivery: Schema.optional(Schema.Literal("response"))
|
|
997
1115
|
}));
|
|
998
1116
|
const SubscribeCommand = command("subscription.subscribe", SubscriptionScope);
|
|
999
1117
|
const UnsubscribeCommand = command("subscription.unsubscribe", SubscriptionScope);
|
|
1000
1118
|
const MapPingCommand = command("map-ping.send", MapPingSendPayloadSchema);
|
|
1119
|
+
const BattlePingCommand = command("battle-ping.send", BattlePingSendPayloadSchema);
|
|
1120
|
+
const LootSplitCommand = command("loot-split.send", LootSplitSendPayloadSchema);
|
|
1121
|
+
const LootSplitAutoAcceptCommand = command("loot-split.auto-accept", LootSplitAutoAcceptPayloadSchema);
|
|
1001
1122
|
const AirTagSubscriptionCommand = command("air-tag.subscription", Schema.Struct({
|
|
1002
1123
|
requestId: RequestId,
|
|
1003
1124
|
enabled: Schema.Boolean,
|
|
1004
1125
|
expectedMapId: Schema.optional(NonNegativeInt)
|
|
1005
1126
|
}));
|
|
1006
1127
|
const AirTagObservationCommand = command("air-tag.observation", AirTagObservationBatchSchema);
|
|
1128
|
+
const AirTagMapThreatsFetchCommand = command("air-tag.map-threats.fetch", Schema.Struct({
|
|
1129
|
+
organizationId: Schema.NonEmptyString,
|
|
1130
|
+
world: Schema.NonEmptyString
|
|
1131
|
+
}));
|
|
1132
|
+
const AirTagMapThreatsFetchResponse = Schema.Struct({ threats: Schema.Array(AirTagMapThreatEventSchema) });
|
|
1133
|
+
const NpcPresenceReportCommand = command("npc-presence.report", NpcPresenceReportSchema);
|
|
1134
|
+
const NpcPresenceFetchCommand = command("npc-presence.fetch", Schema.Struct({
|
|
1135
|
+
organizationId: Schema.NonEmptyString,
|
|
1136
|
+
world: Schema.NonEmptyString
|
|
1137
|
+
}));
|
|
1007
1138
|
const AirTagRejectCode = Schema.Literals([
|
|
1008
1139
|
"forbidden",
|
|
1009
1140
|
"invalid-context",
|
|
@@ -1032,17 +1163,24 @@ const AirTagObservationAck = Schema.Union([Schema.Struct({
|
|
|
1032
1163
|
const ClientCommand = Schema.Union([
|
|
1033
1164
|
SessionJoinCommand,
|
|
1034
1165
|
HeartbeatCommand,
|
|
1166
|
+
PingCommand,
|
|
1035
1167
|
PresencePublishCommand,
|
|
1036
1168
|
PresenceFetchCommand,
|
|
1037
1169
|
SubscribeCommand,
|
|
1038
1170
|
UnsubscribeCommand,
|
|
1039
1171
|
MapPingCommand,
|
|
1172
|
+
BattlePingCommand,
|
|
1173
|
+
LootSplitCommand,
|
|
1174
|
+
LootSplitAutoAcceptCommand,
|
|
1040
1175
|
AirTagSubscriptionCommand,
|
|
1041
|
-
AirTagObservationCommand
|
|
1176
|
+
AirTagObservationCommand,
|
|
1177
|
+
AirTagMapThreatsFetchCommand,
|
|
1178
|
+
NpcPresenceReportCommand,
|
|
1179
|
+
NpcPresenceFetchCommand
|
|
1042
1180
|
]);
|
|
1043
1181
|
const RealtimeError = Schema.Struct({
|
|
1044
|
-
code: NonEmptyString,
|
|
1045
|
-
message: NonEmptyString,
|
|
1182
|
+
code: Schema.NonEmptyString,
|
|
1183
|
+
message: Schema.NonEmptyString,
|
|
1046
1184
|
retryable: Schema.Boolean,
|
|
1047
1185
|
retryAfterMs: Schema.optional(NonNegativeInt),
|
|
1048
1186
|
details: Schema.optional(Schema.Record(Schema.String, Schema.Unknown))
|
|
@@ -1065,19 +1203,21 @@ const serverEvent = (type, data) => Schema.Struct({
|
|
|
1065
1203
|
data
|
|
1066
1204
|
});
|
|
1067
1205
|
const OrganizationEvent = Schema.Struct({
|
|
1068
|
-
organizationId: NonEmptyString,
|
|
1206
|
+
organizationId: Schema.NonEmptyString,
|
|
1069
1207
|
payload: Schema.Unknown
|
|
1070
1208
|
});
|
|
1071
1209
|
const ServerEvent = Schema.Union([
|
|
1210
|
+
serverEvent("session.hello", Schema.Struct({ connectionId: Schema.NonEmptyString })),
|
|
1072
1211
|
serverEvent("session.joined", Schema.Struct({
|
|
1073
|
-
connectionId: NonEmptyString,
|
|
1074
|
-
organizationIds: Schema.Array(NonEmptyString),
|
|
1212
|
+
connectionId: Schema.NonEmptyString,
|
|
1213
|
+
organizationIds: Schema.Array(Schema.NonEmptyString),
|
|
1075
1214
|
subscriptionScopes: Schema.Array(SubscriptionScope),
|
|
1076
|
-
accessPolicy: Schema.optional(AccessPolicySnapshot)
|
|
1215
|
+
accessPolicy: Schema.optional(AccessPolicySnapshot),
|
|
1216
|
+
capabilities: Schema.optional(Schema.Array(Schema.NonEmptyString))
|
|
1077
1217
|
})),
|
|
1078
1218
|
serverEvent("permissions.updated", Schema.Struct({
|
|
1079
1219
|
changes: Schema.optional(Schema.Array(AccessPolicyChange)),
|
|
1080
|
-
organizationIds: Schema.Array(NonEmptyString),
|
|
1220
|
+
organizationIds: Schema.Array(Schema.NonEmptyString),
|
|
1081
1221
|
subscriptionScopes: Schema.Array(SubscriptionScope),
|
|
1082
1222
|
accessPolicy: Schema.optional(AccessPolicySnapshot)
|
|
1083
1223
|
})),
|
|
@@ -1087,12 +1227,24 @@ const ServerEvent = Schema.Union([
|
|
|
1087
1227
|
serverEvent("chat.updated", OrganizationEvent),
|
|
1088
1228
|
serverEvent("chat.deleted", OrganizationEvent),
|
|
1089
1229
|
serverEvent("chat.cleared", OrganizationEvent),
|
|
1230
|
+
serverEvent("global-chat.created", GlobalChatMessageSchema),
|
|
1231
|
+
serverEvent("global-chat.deleted", Schema.Struct({
|
|
1232
|
+
world: Schema.optionalKey(GlobalChatWorld),
|
|
1233
|
+
id: Schema.NonEmptyString
|
|
1234
|
+
})),
|
|
1235
|
+
serverEvent("global-chat.pinned", Schema.Struct({
|
|
1236
|
+
world: Schema.optionalKey(GlobalChatWorld),
|
|
1237
|
+
message: Schema.NullOr(GlobalChatMessageSchema)
|
|
1238
|
+
})),
|
|
1239
|
+
serverEvent("global-chat.stats", GlobalChatStatsSchema),
|
|
1090
1240
|
serverEvent("feed.entry", UserFeedItem),
|
|
1091
|
-
serverEvent("kills.changed", Schema.Struct({ guildId: NonEmptyString })),
|
|
1241
|
+
serverEvent("kills.changed", Schema.Struct({ guildId: Schema.NonEmptyString })),
|
|
1092
1242
|
serverEvent("loot.created", GuildLootCreatedEventV2),
|
|
1093
1243
|
serverEvent("loot.share-updated", GuildLootShareUpdatedEventV2),
|
|
1244
|
+
serverEvent("loot.snapshot", LootSnapshotEvent),
|
|
1094
1245
|
serverEvent("timer.created", OrganizationEvent),
|
|
1095
1246
|
serverEvent("timer.deleted", OrganizationEvent),
|
|
1247
|
+
serverEvent("hero-sighting.updated", OrganizationEvent),
|
|
1096
1248
|
serverEvent("reservation.created", OrganizationEvent),
|
|
1097
1249
|
serverEvent("reservation.deleted", OrganizationEvent),
|
|
1098
1250
|
serverEvent("reservation.changed", ReservationChangedEventV2),
|
|
@@ -1100,19 +1252,29 @@ const ServerEvent = Schema.Union([
|
|
|
1100
1252
|
serverEvent("member-refresh.updated", OrganizationEvent),
|
|
1101
1253
|
serverEvent("party-gathering.updated", OrganizationEvent),
|
|
1102
1254
|
serverEvent("party-gathering.cancelled", OrganizationEvent),
|
|
1255
|
+
serverEvent("party-gathering.state-updated", Schema.Struct({
|
|
1256
|
+
organizationId: Schema.NonEmptyString,
|
|
1257
|
+
payload: PartyGatheringClientUpdateSchema
|
|
1258
|
+
})),
|
|
1103
1259
|
serverEvent("party-ready-room.updated", OrganizationEvent),
|
|
1104
1260
|
serverEvent("map-ping.received", MapPingEventSchema),
|
|
1261
|
+
serverEvent("battle-ping.received", BattlePingEventSchema),
|
|
1262
|
+
serverEvent("loot-split.received", LootSplitEventSchema),
|
|
1263
|
+
serverEvent("loot-split.auto-accepted", LootSplitAutoAcceptedEventSchema),
|
|
1105
1264
|
serverEvent("air-tag.updated", AirTagUpdateEventSchema),
|
|
1265
|
+
serverEvent("air-tag.scope-updated", AirTagScopeUpdateEventSchema),
|
|
1266
|
+
serverEvent("air-tag.map-threat-updated", AirTagMapThreatEventSchema),
|
|
1267
|
+
serverEvent("npc-presence.updated", NpcPresenceEventSchema),
|
|
1106
1268
|
serverEvent("event.map-status-updated", OrganizationEvent),
|
|
1107
1269
|
serverEvent("event.hero-killed", OrganizationEvent),
|
|
1108
1270
|
serverEvent("event.ranking-updated", OrganizationEvent),
|
|
1109
1271
|
serverEvent("event.respawn-window-opened", OrganizationEvent),
|
|
1110
1272
|
serverEvent("event.respawn-window-closed", OrganizationEvent),
|
|
1111
1273
|
serverEvent("notification.volunteer", Schema.Struct({
|
|
1112
|
-
notificationId: NonEmptyString,
|
|
1274
|
+
notificationId: Schema.NonEmptyString,
|
|
1113
1275
|
volunteer: Schema.StructWithRest(Schema.Struct({
|
|
1114
|
-
discordId: NonEmptyString,
|
|
1115
|
-
world: NonEmptyString
|
|
1276
|
+
discordId: Schema.NonEmptyString,
|
|
1277
|
+
world: Schema.NonEmptyString
|
|
1116
1278
|
}), [Schema.Record(Schema.String, Schema.Unknown)])
|
|
1117
1279
|
}))
|
|
1118
1280
|
]);
|
|
@@ -1122,7 +1284,6 @@ const RealtimeFrame = Schema.Union([
|
|
|
1122
1284
|
ServerEvent
|
|
1123
1285
|
]);
|
|
1124
1286
|
Schema.decodeUnknownSync(ClientCommand);
|
|
1125
|
-
Schema.decodeUnknownSync(Response);
|
|
1126
1287
|
Schema.decodeUnknownSync(ServerEvent);
|
|
1127
1288
|
const decodeRealtimeFrame = Schema.decodeUnknownSync(RealtimeFrame);
|
|
1128
1289
|
const serverEventTypes = new Set(ServerEvent.members.map((member) => member.fields.type.literal));
|
|
@@ -1164,11 +1325,34 @@ const tryDecodeRealtimeFrame = (bytes) => Result.try({
|
|
|
1164
1325
|
});
|
|
1165
1326
|
Schema.decodeUnknownSync(PresenceSnapshot);
|
|
1166
1327
|
Schema.is(MapPingAckSchema);
|
|
1328
|
+
Schema.is(LootSplitAckSchema);
|
|
1167
1329
|
Schema.is(AirTagSubscriptionAck);
|
|
1168
1330
|
Schema.is(AirTagObservationAck);
|
|
1331
|
+
Schema.is(AirTagMapThreatsFetchResponse);
|
|
1332
|
+
Schema.is(NpcPresenceReportAckSchema);
|
|
1333
|
+
Schema.is(NpcPresenceSnapshotSchema);
|
|
1334
|
+
const hasRealtimeCapabilities = Schema.is(Schema.Struct({ capabilities: Schema.Array(Schema.NonEmptyString) }));
|
|
1169
1335
|
Schema.is(Schema.Struct({ presences: Schema.optional(PresenceSnapshot.fields.presences) }));
|
|
1170
1336
|
//#endregion
|
|
1337
|
+
//#region ../client/src/realtime/notify-listeners.ts
|
|
1338
|
+
const reportListenerError = ({ source, error }) => {
|
|
1339
|
+
try {
|
|
1340
|
+
console.warn(`[Realtime] ${source} listener failed:`, error);
|
|
1341
|
+
} catch {}
|
|
1342
|
+
};
|
|
1343
|
+
const notifyListeners = (listeners, notify, source) => {
|
|
1344
|
+
for (const listener of listeners) try {
|
|
1345
|
+
notify(listener);
|
|
1346
|
+
} catch (error) {
|
|
1347
|
+
reportListenerError({
|
|
1348
|
+
source,
|
|
1349
|
+
error
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
//#endregion
|
|
1171
1354
|
//#region ../client/src/realtime/realtime-client.ts
|
|
1355
|
+
var RealtimeRequestTimeout = class extends Error {};
|
|
1172
1356
|
var RealtimeRequestError = class extends Error {
|
|
1173
1357
|
code;
|
|
1174
1358
|
retryable;
|
|
@@ -1193,6 +1377,7 @@ const normalizeUrl = (baseUrl, path) => {
|
|
|
1193
1377
|
};
|
|
1194
1378
|
const nativeWebSocketFactory = (url, protocols) => new WebSocket(url, protocols);
|
|
1195
1379
|
const WEBSOCKET_OPEN = 1;
|
|
1380
|
+
const MIN_HEARTBEAT_RETRY_BUDGET_MS = 2e3;
|
|
1196
1381
|
const scopeKey = (scope) => JSON.stringify([
|
|
1197
1382
|
scope.topic,
|
|
1198
1383
|
scope.organizationId ?? null,
|
|
@@ -1206,6 +1391,7 @@ const toBytes = async (data) => {
|
|
|
1206
1391
|
if (data instanceof Blob) return new Uint8Array(await data.arrayBuffer());
|
|
1207
1392
|
throw new Error("Gateway returned a non-binary realtime frame");
|
|
1208
1393
|
};
|
|
1394
|
+
const SERVER_CLOSE_CODES = new Set(Object.values(REALTIME_SERVER_CLOSE_CODES));
|
|
1209
1395
|
const getPresenceSessionId = (data) => {
|
|
1210
1396
|
if (!data || typeof data !== "object" || !("sessionId" in data)) return null;
|
|
1211
1397
|
return typeof data.sessionId === "string" ? data.sessionId : null;
|
|
@@ -1221,15 +1407,22 @@ var RealtimeClient = class {
|
|
|
1221
1407
|
frameEncoding;
|
|
1222
1408
|
eventListeners = /* @__PURE__ */ new Set();
|
|
1223
1409
|
stateListeners = /* @__PURE__ */ new Set();
|
|
1410
|
+
heartbeatLatencyMs = null;
|
|
1411
|
+
heartbeatLatencyListeners = /* @__PURE__ */ new Set();
|
|
1224
1412
|
pending = /* @__PURE__ */ new Map();
|
|
1225
1413
|
subscriptions = /* @__PURE__ */ new Map();
|
|
1226
1414
|
socket = null;
|
|
1227
1415
|
stateValue = "disconnected";
|
|
1228
1416
|
joinData = null;
|
|
1417
|
+
joinAttempt = null;
|
|
1229
1418
|
reconnectAttempt = 0;
|
|
1419
|
+
reconnectNotBeforeMs = 0;
|
|
1230
1420
|
reconnectTimeout = null;
|
|
1231
1421
|
heartbeatTimeout = null;
|
|
1232
1422
|
presenceSessionId = null;
|
|
1423
|
+
presenceRefreshedAt = 0;
|
|
1424
|
+
pingSupported = false;
|
|
1425
|
+
latencyProbeInFlight = false;
|
|
1233
1426
|
manuallyClosed = false;
|
|
1234
1427
|
messageChain = Promise.resolve();
|
|
1235
1428
|
rejoinHandler = null;
|
|
@@ -1259,6 +1452,7 @@ var RealtimeClient = class {
|
|
|
1259
1452
|
this.manuallyClosed = true;
|
|
1260
1453
|
this.clearReconnect();
|
|
1261
1454
|
this.clearHeartbeat();
|
|
1455
|
+
this.pingSupported = false;
|
|
1262
1456
|
this.rejectPending(/* @__PURE__ */ new Error("Realtime client disconnected"));
|
|
1263
1457
|
const activeSocket = this.socket;
|
|
1264
1458
|
this.socket = null;
|
|
@@ -1274,6 +1468,30 @@ var RealtimeClient = class {
|
|
|
1274
1468
|
listener(this.stateValue);
|
|
1275
1469
|
return () => this.stateListeners.delete(listener);
|
|
1276
1470
|
}
|
|
1471
|
+
subscribeHeartbeatLatency(listener) {
|
|
1472
|
+
this.heartbeatLatencyListeners.add(listener);
|
|
1473
|
+
listener(this.heartbeatLatencyMs);
|
|
1474
|
+
return () => this.heartbeatLatencyListeners.delete(listener);
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Measures the round trip now instead of at the next heartbeat. A gateway
|
|
1478
|
+
* that does not advertise `connection.ping` keeps the heartbeat measurement.
|
|
1479
|
+
*/
|
|
1480
|
+
probeLatency() {
|
|
1481
|
+
if (!this.pingSupported || this.stateValue !== "ready" || this.latencyProbeInFlight) return;
|
|
1482
|
+
const socket = this.socket;
|
|
1483
|
+
const startedAt = performance.now();
|
|
1484
|
+
this.latencyProbeInFlight = true;
|
|
1485
|
+
this.request("connection.ping", {}).then(() => {
|
|
1486
|
+
if (this.socket === socket) this.setHeartbeatLatency(Math.round(performance.now() - startedAt));
|
|
1487
|
+
}).catch(() => {}).finally(() => {
|
|
1488
|
+
this.latencyProbeInFlight = false;
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1491
|
+
setHeartbeatLatency(latencyMs) {
|
|
1492
|
+
this.heartbeatLatencyMs = latencyMs;
|
|
1493
|
+
notifyListeners(this.heartbeatLatencyListeners, (listener) => listener(latencyMs), "heartbeat latency");
|
|
1494
|
+
}
|
|
1277
1495
|
setReconnectHandler(handler) {
|
|
1278
1496
|
this.rejoinHandler = handler;
|
|
1279
1497
|
}
|
|
@@ -1292,16 +1510,20 @@ var RealtimeClient = class {
|
|
|
1292
1510
|
return this.request("subscription.unsubscribe", scope);
|
|
1293
1511
|
}
|
|
1294
1512
|
request(type, data) {
|
|
1513
|
+
return this.requestWithTimeout(type, data, this.requestTimeoutMs);
|
|
1514
|
+
}
|
|
1515
|
+
requestWithTimeout(type, data, timeoutMs) {
|
|
1295
1516
|
const activeSocket = this.socket;
|
|
1296
1517
|
if (!activeSocket || activeSocket.readyState !== WEBSOCKET_OPEN) return Promise.reject(/* @__PURE__ */ new Error("Realtime connection is not open"));
|
|
1297
1518
|
const requestId = crypto.randomUUID();
|
|
1298
1519
|
return new Promise((resolve, reject) => {
|
|
1299
1520
|
const timeout = setTimeout(() => {
|
|
1300
1521
|
this.pending.delete(requestId);
|
|
1301
|
-
reject(
|
|
1302
|
-
},
|
|
1522
|
+
reject(new RealtimeRequestTimeout(`Realtime request timed out: ${type}`));
|
|
1523
|
+
}, timeoutMs);
|
|
1303
1524
|
this.pending.set(requestId, {
|
|
1304
1525
|
type,
|
|
1526
|
+
startedAt: performance.now(),
|
|
1305
1527
|
resolve,
|
|
1306
1528
|
reject,
|
|
1307
1529
|
timeout
|
|
@@ -1338,39 +1560,71 @@ var RealtimeClient = class {
|
|
|
1338
1560
|
socket.addEventListener("open", () => {
|
|
1339
1561
|
if (this.socket !== socket) return;
|
|
1340
1562
|
this.setState("connected");
|
|
1341
|
-
if (this.joinData) (this.rejoinHandler ?? (() => this.performJoin().then(() => void 0)))().catch(() => socket.close(
|
|
1563
|
+
if (this.joinData && (this.rejoinHandler || this.stateValue === "connected")) (this.rejoinHandler ?? (() => this.performJoin().then(() => void 0)))().catch(() => socket.close(REALTIME_CLIENT_CLOSE_CODES.sessionJoinFailed, "session rejoin failed"));
|
|
1342
1564
|
});
|
|
1343
1565
|
socket.addEventListener("message", (event) => {
|
|
1344
|
-
this.messageChain = this.messageChain.then(() => this.handleMessage(event.data)).catch(() => socket.close(
|
|
1345
|
-
});
|
|
1346
|
-
socket.addEventListener("error", () => {
|
|
1347
|
-
if (this.socket === socket) socket.close();
|
|
1566
|
+
this.messageChain = this.messageChain.then(() => this.handleMessage(event.data, socket)).catch(() => socket.close(REALTIME_CLIENT_CLOSE_CODES.malformedFrame, "malformed realtime frame"));
|
|
1348
1567
|
});
|
|
1349
|
-
socket.addEventListener("close", () => {
|
|
1568
|
+
socket.addEventListener("close", (event) => {
|
|
1350
1569
|
if (this.socket !== socket) return;
|
|
1351
1570
|
this.socket = null;
|
|
1571
|
+
if (event.code !== void 0 && SERVER_CLOSE_CODES.has(event.code)) this.reconnectNotBeforeMs = Math.round(this.random() * REALTIME_SERVER_CLOSE_RECONNECT_WINDOW_MS);
|
|
1352
1572
|
this.clearHeartbeat();
|
|
1573
|
+
this.pingSupported = false;
|
|
1353
1574
|
this.rejectPending(/* @__PURE__ */ new Error("Realtime connection closed"));
|
|
1354
1575
|
this.setState("disconnected");
|
|
1355
1576
|
if (!this.manuallyClosed) this.scheduleReconnect();
|
|
1356
1577
|
});
|
|
1357
1578
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1579
|
+
performJoin(data = this.joinData) {
|
|
1580
|
+
const socket = this.socket;
|
|
1581
|
+
if (!data || !socket || !this.connected) return Promise.resolve(void 0);
|
|
1582
|
+
const key = JSON.stringify(data);
|
|
1583
|
+
const pending = this.joinAttempt;
|
|
1584
|
+
if (pending?.socket === socket) {
|
|
1585
|
+
if (pending.key === key) return pending.promise;
|
|
1586
|
+
return pending.promise.then(() => {
|
|
1587
|
+
if (this.socket !== socket || !this.connected) throw new Error("Realtime connection changed before queued join");
|
|
1588
|
+
return this.performJoin(data);
|
|
1589
|
+
});
|
|
1590
|
+
}
|
|
1591
|
+
const promise = this.restoreSession(socket, this.request("session.join", data));
|
|
1592
|
+
this.joinAttempt = {
|
|
1593
|
+
socket,
|
|
1594
|
+
key,
|
|
1595
|
+
promise
|
|
1596
|
+
};
|
|
1360
1597
|
this.setState("joining");
|
|
1598
|
+
return promise;
|
|
1599
|
+
}
|
|
1600
|
+
async restoreSession(socket, joined) {
|
|
1361
1601
|
try {
|
|
1362
|
-
const result = await
|
|
1602
|
+
const result = await joined;
|
|
1603
|
+
if (this.socket !== socket) return result;
|
|
1604
|
+
this.pingSupported = hasRealtimeCapabilities(result) && result.capabilities.includes("connection.ping");
|
|
1363
1605
|
await Promise.all([...this.subscriptions.values()].map((scope) => this.request("subscription.subscribe", scope)));
|
|
1606
|
+
if (this.socket !== socket) return result;
|
|
1364
1607
|
this.reconnectAttempt = 0;
|
|
1365
1608
|
this.setState("ready");
|
|
1366
1609
|
return result;
|
|
1367
1610
|
} catch (error) {
|
|
1368
|
-
if (this.
|
|
1611
|
+
if (this.socket === socket) {
|
|
1612
|
+
if (error instanceof RealtimeRequestError && !error.retryable) {
|
|
1613
|
+
this.manuallyClosed = true;
|
|
1614
|
+
this.clearReconnect();
|
|
1615
|
+
}
|
|
1616
|
+
if (error instanceof RealtimeRequestError && error.retryable) this.reconnectNotBeforeMs = error.retryAfterMs ?? 0;
|
|
1617
|
+
socket.close(REALTIME_CLIENT_CLOSE_CODES.sessionJoinFailed, "session join failed");
|
|
1618
|
+
}
|
|
1369
1619
|
throw error;
|
|
1620
|
+
} finally {
|
|
1621
|
+
if (this.joinAttempt?.socket === socket) this.joinAttempt = null;
|
|
1370
1622
|
}
|
|
1371
1623
|
}
|
|
1372
|
-
async handleMessage(data) {
|
|
1624
|
+
async handleMessage(data, socket) {
|
|
1625
|
+
if (this.socket !== socket) return;
|
|
1373
1626
|
const frame = await this.decodeFrame(data);
|
|
1627
|
+
if (this.socket !== socket) return;
|
|
1374
1628
|
if ("status" in frame) {
|
|
1375
1629
|
const pending = this.pending.get(frame.requestId);
|
|
1376
1630
|
if (!pending) return;
|
|
@@ -1379,6 +1633,7 @@ var RealtimeClient = class {
|
|
|
1379
1633
|
if (frame.status === "success") {
|
|
1380
1634
|
if (pending.type === "presence.publish") {
|
|
1381
1635
|
this.presenceSessionId = getPresenceSessionId(frame.data);
|
|
1636
|
+
this.presenceRefreshedAt = pending.startedAt;
|
|
1382
1637
|
this.scheduleHeartbeat();
|
|
1383
1638
|
}
|
|
1384
1639
|
pending.resolve(frame.data);
|
|
@@ -1386,7 +1641,7 @@ var RealtimeClient = class {
|
|
|
1386
1641
|
return;
|
|
1387
1642
|
}
|
|
1388
1643
|
if (!isServerEventFrame(frame)) return;
|
|
1389
|
-
|
|
1644
|
+
notifyListeners(this.eventListeners, (listener) => listener(frame), `server event ${frame.type}`);
|
|
1390
1645
|
}
|
|
1391
1646
|
encodeFrame(frame) {
|
|
1392
1647
|
return this.frameEncoding === "json" ? JSON.stringify(frame) : encodeRealtimeFrame(frame);
|
|
@@ -1403,18 +1658,59 @@ var RealtimeClient = class {
|
|
|
1403
1658
|
scheduleReconnect() {
|
|
1404
1659
|
this.reconnectAttempt += 1;
|
|
1405
1660
|
const exponential = Math.min(this.reconnectMaxDelayMs, this.reconnectBaseDelayMs * 2 ** (this.reconnectAttempt - 1));
|
|
1406
|
-
const jittered = Math.round(exponential * (.5 + this.random()));
|
|
1661
|
+
const jittered = Math.max(Math.round(exponential * (.5 + this.random())), this.reconnectNotBeforeMs);
|
|
1662
|
+
this.reconnectNotBeforeMs = 0;
|
|
1407
1663
|
this.setState("reconnecting");
|
|
1408
1664
|
this.reconnectTimeout = setTimeout(() => this.open("reconnecting"), jittered);
|
|
1409
1665
|
}
|
|
1410
|
-
scheduleHeartbeat() {
|
|
1666
|
+
scheduleHeartbeat(delayMs = PRESENCE_HEARTBEAT_INTERVAL_MS, retryAttempt = 0) {
|
|
1411
1667
|
this.clearHeartbeat();
|
|
1412
|
-
if (!this.presenceSessionId || !this.connected)
|
|
1668
|
+
if (!this.presenceSessionId || !this.connected) {
|
|
1669
|
+
this.setHeartbeatLatency(null);
|
|
1670
|
+
return;
|
|
1671
|
+
}
|
|
1413
1672
|
this.heartbeatTimeout = setTimeout(() => {
|
|
1414
1673
|
const sessionId = this.presenceSessionId;
|
|
1415
1674
|
if (!sessionId) return;
|
|
1416
|
-
|
|
1417
|
-
|
|
1675
|
+
const startedAt = performance.now();
|
|
1676
|
+
const socket = this.socket;
|
|
1677
|
+
const refreshedAt = this.presenceRefreshedAt;
|
|
1678
|
+
const expiry = refreshedAt + PRESENCE_EXPIRY_MS;
|
|
1679
|
+
const remaining = expiry - startedAt;
|
|
1680
|
+
if (remaining <= 0 || retryAttempt > 0 && remaining < MIN_HEARTBEAT_RETRY_BUDGET_MS) {
|
|
1681
|
+
this.setHeartbeatLatency(null);
|
|
1682
|
+
socket?.close(REALTIME_CLIENT_CLOSE_CODES.heartbeatUnavailable, "heartbeat unavailable");
|
|
1683
|
+
return;
|
|
1684
|
+
}
|
|
1685
|
+
const isStaleHeartbeat = () => this.socket !== socket || this.presenceSessionId !== sessionId || this.presenceRefreshedAt !== refreshedAt;
|
|
1686
|
+
this.requestWithTimeout("presence.heartbeat", { sessionId }, Math.min(this.requestTimeoutMs, remaining)).then(() => {
|
|
1687
|
+
if (isStaleHeartbeat()) return;
|
|
1688
|
+
this.presenceRefreshedAt = startedAt;
|
|
1689
|
+
this.setHeartbeatLatency(Math.round(performance.now() - startedAt));
|
|
1690
|
+
this.scheduleHeartbeat();
|
|
1691
|
+
}).catch((error) => {
|
|
1692
|
+
if (isStaleHeartbeat()) return;
|
|
1693
|
+
this.setHeartbeatLatency(null);
|
|
1694
|
+
if (error instanceof RealtimeRequestError && error.retryable) {
|
|
1695
|
+
const retryDelay = Math.max(Math.round(Math.min(5e3, 1e3 * 2 ** retryAttempt) * (.5 + this.random())), error.retryAfterMs ?? 0);
|
|
1696
|
+
if (expiry - performance.now() - retryDelay >= MIN_HEARTBEAT_RETRY_BUDGET_MS) {
|
|
1697
|
+
this.scheduleHeartbeat(retryDelay, retryAttempt + 1);
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
socket?.close(REALTIME_CLIENT_CLOSE_CODES.heartbeatUnavailable, "heartbeat unavailable");
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
if (error instanceof RealtimeRequestTimeout) {
|
|
1704
|
+
socket?.close(REALTIME_CLIENT_CLOSE_CODES.heartbeatTimeout, "heartbeat timeout");
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
if (error instanceof RealtimeRequestError) {
|
|
1708
|
+
socket?.close(REALTIME_CLIENT_CLOSE_CODES.heartbeatRejected, "heartbeat rejected");
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
socket?.close();
|
|
1712
|
+
});
|
|
1713
|
+
}, delayMs);
|
|
1418
1714
|
}
|
|
1419
1715
|
rejectPending(error) {
|
|
1420
1716
|
for (const pending of this.pending.values()) {
|
|
@@ -1426,7 +1722,8 @@ var RealtimeClient = class {
|
|
|
1426
1722
|
setState(state) {
|
|
1427
1723
|
if (this.stateValue === state) return;
|
|
1428
1724
|
this.stateValue = state;
|
|
1429
|
-
|
|
1725
|
+
if (state === "disconnected") this.setHeartbeatLatency(null);
|
|
1726
|
+
notifyListeners(this.stateListeners, (listener) => listener(state), `connection state ${state}`);
|
|
1430
1727
|
}
|
|
1431
1728
|
clearReconnect() {
|
|
1432
1729
|
if (this.reconnectTimeout) clearTimeout(this.reconnectTimeout);
|