Skip to content

Commit 4ff6fb9

Browse files
committed
use sticky events for matrixRTC
Signed-off-by: Timo K <[email protected]>
1 parent 346d82a commit 4ff6fb9

File tree

3 files changed

+78
-35
lines changed

3 files changed

+78
-35
lines changed

src/matrixrtc/CallMembership.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,10 @@ export type SessionMembershipData = {
8686
* (for example caused by a homeserver crashes)
8787
**/
8888
expires?: number;
89+
/**
90+
* the sticky key for sticky events packed application + device_id making up the used slot + device.
91+
*/
92+
sticky_key?: string;
8993
};
9094

9195
const checkSessionsMembershipData = (data: any, errors: string[]): data is SessionMembershipData => {

src/matrixrtc/MatrixRTCSession.ts

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ limitations under the License.
1717
import { type Logger, logger as rootLogger } from "../logger.ts";
1818
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
1919
import { EventTimeline } from "../models/event-timeline.ts";
20-
import { type Room } from "../models/room.ts";
20+
import { RoomEvent, type Room } from "../models/room.ts";
2121
import { type MatrixClient } from "../client.ts";
2222
import { EventType, RelationType } from "../@types/event.ts";
2323
import { KnownMembership } from "../@types/membership.ts";
@@ -49,6 +49,7 @@ import {
4949
} from "./RoomAndToDeviceKeyTransport.ts";
5050
import { TypedReEmitter } from "../ReEmitter.ts";
5151
import { ToDeviceKeyTransport } from "./ToDeviceKeyTransport.ts";
52+
import { type MatrixEvent } from "src/matrix.ts";
5253

5354
/**
5455
* Events emitted by MatrixRTCSession
@@ -285,7 +286,7 @@ export class MatrixRTCSession extends TypedEventEmitter<
285286
* @deprecated Use `MatrixRTCSession.sessionMembershipsForRoom` instead.
286287
*/
287288
public static callMembershipsForRoom(
288-
room: Pick<Room, "getLiveTimeline" | "roomId" | "hasMembershipState">,
289+
room: Pick<Room, "getLiveTimeline" | "roomId" | "hasMembershipState" | "getStickyEventsMap">,
289290
): CallMembership[] {
290291
return MatrixRTCSession.sessionMembershipsForRoom(room, {
291292
id: "",
@@ -298,16 +299,24 @@ export class MatrixRTCSession extends TypedEventEmitter<
298299
* oldest first.
299300
*/
300301
public static sessionMembershipsForRoom(
301-
room: Pick<Room, "getLiveTimeline" | "roomId" | "hasMembershipState">,
302+
room: Pick<Room, "getLiveTimeline" | "roomId" | "hasMembershipState" | "getStickyEventsMap">,
302303
sessionDescription: SessionDescription,
304+
useStickyEvents: boolean = true,
303305
): CallMembership[] {
304306
const logger = rootLogger.getChild(`[MatrixRTCSession ${room.roomId}]`);
305-
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
306-
if (!roomState) {
307-
logger.warn("Couldn't get state for room " + room.roomId);
308-
throw new Error("Could't get state for room " + room.roomId);
307+
let callMemberEvents;
308+
if (useStickyEvents) {
309+
callMemberEvents = Array.from(room.getStickyEventsMap().values()).filter(
310+
(e) => e.getType() === EventType.GroupCallMemberPrefix,
311+
);
312+
} else {
313+
const roomState = room.getLiveTimeline().getState(EventTimeline.FORWARDS);
314+
if (!roomState) {
315+
logger.warn("Couldn't get state for room " + room.roomId);
316+
throw new Error("Could't get state for room " + room.roomId);
317+
}
318+
callMemberEvents = roomState.getStateEvents(EventType.GroupCallMemberPrefix);
309319
}
310-
const callMemberEvents = roomState.getStateEvents(EventType.GroupCallMemberPrefix);
311320

312321
const callMemberships: CallMembership[] = [];
313322
for (const memberEvent of callMemberEvents) {
@@ -422,10 +431,10 @@ export class MatrixRTCSession extends TypedEventEmitter<
422431
MatrixClient,
423432
| "getUserId"
424433
| "getDeviceId"
425-
| "sendStateEvent"
426-
| "_unstable_sendDelayedStateEvent"
427-
| "_unstable_updateDelayedEvent"
428434
| "sendEvent"
435+
| "_unstable_updateDelayedEvent"
436+
| "_unstable_sendStickyEvent"
437+
| "_unstable_sendStickyDelayedEvent"
429438
| "cancelPendingEvent"
430439
| "encryptAndSendToDevice"
431440
| "off"
@@ -449,9 +458,10 @@ export class MatrixRTCSession extends TypedEventEmitter<
449458
const roomState = this.roomSubset.getLiveTimeline().getState(EventTimeline.FORWARDS);
450459
// TODO: double check if this is actually needed. Should be covered by refreshRoom in MatrixRTCSessionManager
451460
roomState?.on(RoomStateEvent.Members, this.onRoomMemberUpdate);
461+
this.roomSubset.on(RoomEvent.StickyEvents, this.onStickyEventUpdate);
462+
452463
this.setExpiryTimer();
453464
}
454-
455465
/*
456466
* Returns true if we intend to be participating in the MatrixRTC session.
457467
* This is determined by checking if the relativeExpiry has been set.
@@ -471,6 +481,7 @@ export class MatrixRTCSession extends TypedEventEmitter<
471481
}
472482
const roomState = this.roomSubset.getLiveTimeline().getState(EventTimeline.FORWARDS);
473483
roomState?.off(RoomStateEvent.Members, this.onRoomMemberUpdate);
484+
this.roomSubset.off(RoomEvent.StickyEvents, this.onStickyEventUpdate);
474485
}
475486
private reEmitter = new TypedReEmitter<
476487
MatrixRTCSessionEvent | RoomAndToDeviceEvents | MembershipManagerEvent,
@@ -718,6 +729,12 @@ export class MatrixRTCSession extends TypedEventEmitter<
718729
this.recalculateSessionMembers();
719730
};
720731

732+
private onStickyEventUpdate = (stickyEvents: Map<string, MatrixEvent>, room: Room): void => {
733+
if (Array.from(stickyEvents.values()).some((e) => e.getType() === EventType.GroupCallMemberPrefix)) {
734+
this.recalculateSessionMembers();
735+
}
736+
};
737+
721738
/**
722739
* Call this when something changed that may impacts the current MatrixRTC members in this session.
723740
*/

src/matrixrtc/MembershipManager.ts

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
type IMembershipManager,
3535
type MembershipManagerEventHandlerMap,
3636
} from "./IMembershipManager.ts";
37+
import { type EmptyObject } from "src/matrix.ts";
3738

3839
/* MembershipActionTypes:
3940
@@ -77,6 +78,8 @@ On Leave: ───────── STOP ALL ABOVE
7778
(s) Successful restart/resend
7879
*/
7980

81+
const STICK_DURATION_MS = 60 * 60 * 1000; // 60 minutes
82+
8083
/**
8184
* The different types of actions the MembershipManager can take.
8285
* @internal
@@ -295,9 +298,9 @@ export class MembershipManager
295298
MatrixClient,
296299
| "getUserId"
297300
| "getDeviceId"
298-
| "sendStateEvent"
299-
| "_unstable_sendDelayedStateEvent"
300301
| "_unstable_updateDelayedEvent"
302+
| "_unstable_sendStickyEvent"
303+
| "_unstable_sendStickyDelayedEvent"
301304
>,
302305
private getOldestMembership: () => CallMembership | undefined,
303306
public readonly sessionDescription: SessionDescription,
@@ -371,7 +374,11 @@ export class MembershipManager
371374
return this.joinConfig?.membershipEventExpiryHeadroomMs ?? 5_000;
372375
}
373376
private computeNextExpiryActionTs(iteration: number): number {
374-
return this.state.startTime + this.membershipEventExpiryMs * iteration - this.membershipEventExpiryHeadroomMs;
377+
return (
378+
this.state.startTime +
379+
Math.min(this.membershipEventExpiryMs, STICK_DURATION_MS) * iteration -
380+
this.membershipEventExpiryHeadroomMs
381+
);
375382
}
376383
private get delayedLeaveEventDelayMs(): number {
377384
return this.delayedLeaveEventDelayMsOverride ?? this.joinConfig?.delayedLeaveEventDelayMs ?? 8_000;
@@ -450,14 +457,15 @@ export class MembershipManager
450457
// (Another client could have canceled it, the homeserver might have removed/lost it due to a restart, ...)
451458
// In the `then` and `catch` block we treat both cases differently. "if (this.state.hasMemberStateEvent) {} else {}"
452459
return await this.client
453-
._unstable_sendDelayedStateEvent(
460+
._unstable_sendStickyDelayedEvent(
454461
this.room.roomId,
462+
STICK_DURATION_MS,
455463
{
456464
delay: this.delayedLeaveEventDelayMs,
457465
},
466+
null,
458467
EventType.GroupCallMemberPrefix,
459-
{}, // leave event
460-
this.stateKey,
468+
{ sticky_key: this.stateKey } as EmptyObject & { sticky_key: string }, // leave event
461469
)
462470
.then((response) => {
463471
this.state.expectedServerDelayLeaveTs = Date.now() + this.delayedLeaveEventDelayMs;
@@ -482,7 +490,7 @@ export class MembershipManager
482490
if (this.manageMaxDelayExceededSituation(e)) {
483491
return createInsertActionUpdate(repeatActionType);
484492
}
485-
const update = this.actionUpdateFromErrors(e, repeatActionType, "sendDelayedStateEvent");
493+
const update = this.actionUpdateFromErrors(e, repeatActionType, "_unstable_sendStickyDelayedEvent");
486494
if (update) return update;
487495

488496
if (this.state.hasMemberStateEvent) {
@@ -640,12 +648,10 @@ export class MembershipManager
640648

641649
private async sendJoinEvent(): Promise<ActionUpdate> {
642650
return await this.client
643-
.sendStateEvent(
644-
this.room.roomId,
645-
EventType.GroupCallMemberPrefix,
646-
this.makeMyMembership(this.membershipEventExpiryMs),
647-
this.stateKey,
648-
)
651+
._unstable_sendStickyEvent(this.room.roomId, STICK_DURATION_MS, null, EventType.GroupCallMemberPrefix, {
652+
...this.makeMyMembership(this.membershipEventExpiryMs),
653+
sticky_key: this.stateKey,
654+
})
649655
.then(() => {
650656
this.setAndEmitProbablyLeft(false);
651657
this.state.startTime = Date.now();
@@ -677,7 +683,11 @@ export class MembershipManager
677683
};
678684
})
679685
.catch((e) => {
680-
const update = this.actionUpdateFromErrors(e, MembershipActionType.SendJoinEvent, "sendStateEvent");
686+
const update = this.actionUpdateFromErrors(
687+
e,
688+
MembershipActionType.SendJoinEvent,
689+
"_unstable_sendStickyEvent",
690+
);
681691
if (update) return update;
682692
throw e;
683693
});
@@ -686,12 +696,10 @@ export class MembershipManager
686696
private async updateExpiryOnJoinedEvent(): Promise<ActionUpdate> {
687697
const nextExpireUpdateIteration = this.state.expireUpdateIterations + 1;
688698
return await this.client
689-
.sendStateEvent(
690-
this.room.roomId,
691-
EventType.GroupCallMemberPrefix,
692-
this.makeMyMembership(this.membershipEventExpiryMs * nextExpireUpdateIteration),
693-
this.stateKey,
694-
)
699+
._unstable_sendStickyEvent(this.room.roomId, STICK_DURATION_MS, null, EventType.GroupCallMemberPrefix, {
700+
...this.makeMyMembership(this.membershipEventExpiryMs),
701+
sticky_key: this.stateKey,
702+
})
695703
.then(() => {
696704
// Success, we reset retries and schedule update.
697705
this.resetRateLimitCounter(MembershipActionType.UpdateExpiry);
@@ -706,22 +714,36 @@ export class MembershipManager
706714
};
707715
})
708716
.catch((e) => {
709-
const update = this.actionUpdateFromErrors(e, MembershipActionType.UpdateExpiry, "sendStateEvent");
717+
const update = this.actionUpdateFromErrors(
718+
e,
719+
MembershipActionType.UpdateExpiry,
720+
"_unstable_sendStickyEvent",
721+
);
710722
if (update) return update;
711723

712724
throw e;
713725
});
714726
}
715727
private async sendFallbackLeaveEvent(): Promise<ActionUpdate> {
716728
return await this.client
717-
.sendStateEvent(this.room.roomId, EventType.GroupCallMemberPrefix, {}, this.stateKey)
729+
._unstable_sendStickyEvent(
730+
this.room.roomId,
731+
STICK_DURATION_MS,
732+
null,
733+
EventType.GroupCallMemberPrefix,
734+
{ sticky_key: this.stateKey } as EmptyObject & { sticky_key: string }, // leave event
735+
)
718736
.then(() => {
719737
this.resetRateLimitCounter(MembershipActionType.SendLeaveEvent);
720738
this.state.hasMemberStateEvent = false;
721739
return { replace: [] };
722740
})
723741
.catch((e) => {
724-
const update = this.actionUpdateFromErrors(e, MembershipActionType.SendLeaveEvent, "sendStateEvent");
742+
const update = this.actionUpdateFromErrors(
743+
e,
744+
MembershipActionType.SendLeaveEvent,
745+
"_unstable_sendStickyEvent",
746+
);
725747
if (update) return update;
726748
throw e;
727749
});

0 commit comments

Comments
 (0)