Global voice-indicators (#2020)

* make use of well known types for PlayerDetailsUpdated message

* show voice-chat indicator of people from other groups too

* cleanup

* check for outline value

* do not send outline color if undefined

* revert removing LocalVolumeStore

* use auto-generated type instead

Co-authored-by: Piotr 'pwh' Hanusiak <p.hanusiak@workadventu.re>
This commit is contained in:
Piotr Hanusiak 2022-04-04 13:47:23 +02:00 committed by GitHub
parent 2ff7bf54ae
commit 7e84ac5454
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 71 additions and 69 deletions

View File

@ -210,11 +210,7 @@ export class GameRoom {
} }
updatePlayerDetails(user: User, playerDetailsMessage: SetPlayerDetailsMessage) { updatePlayerDetails(user: User, playerDetailsMessage: SetPlayerDetailsMessage) {
if (playerDetailsMessage.getRemoveoutlinecolor()) { user.updateDetails(playerDetailsMessage);
user.outlineColor = undefined;
} else {
user.outlineColor = playerDetailsMessage.getOutlinecolor();
}
} }
private updateUserGroup(user: User): void { private updateUserGroup(user: User): void {

View File

@ -15,6 +15,7 @@ import {
SubMessage, SubMessage,
} from "../Messages/generated/messages_pb"; } from "../Messages/generated/messages_pb";
import { CharacterLayer } from "_Model/Websocket/CharacterLayer"; import { CharacterLayer } from "_Model/Websocket/CharacterLayer";
import { BoolValue, UInt32Value } from "google-protobuf/google/protobuf/wrappers_pb";
export type UserSocket = ServerDuplexStream<PusherToBackMessage, ServerToClientMessage>; export type UserSocket = ServerDuplexStream<PusherToBackMessage, ServerToClientMessage>;
@ -37,7 +38,8 @@ export class User implements Movable {
public readonly name: string, public readonly name: string,
public readonly characterLayers: CharacterLayer[], public readonly characterLayers: CharacterLayer[],
public readonly companion?: CompanionMessage, public readonly companion?: CompanionMessage,
private _outlineColor?: number | undefined private outlineColor?: number,
private voiceIndicatorShown?: boolean
) { ) {
this.listenedZones = new Set<Zone>(); this.listenedZones = new Set<Zone>();
@ -83,6 +85,10 @@ export class User implements Movable {
return this.followedBy.size !== 0; return this.followedBy.size !== 0;
} }
public getOutlineColor(): number | undefined {
return this.outlineColor;
}
get following(): User | undefined { get following(): User | undefined {
return this._following; return this._following;
} }
@ -115,14 +121,21 @@ export class User implements Movable {
} }
} }
public set outlineColor(value: number | undefined) { public updateDetails(details: SetPlayerDetailsMessage) {
this._outlineColor = value; if (details.getRemoveoutlinecolor()) {
this.outlineColor = undefined;
} else if (details.getOutlinecolor()?.getValue() !== undefined) {
this.outlineColor = details.getOutlinecolor()?.getValue();
}
this.voiceIndicatorShown = details.getShowvoiceindicator()?.getValue();
const playerDetails = new SetPlayerDetailsMessage(); const playerDetails = new SetPlayerDetailsMessage();
if (value === undefined) {
playerDetails.setRemoveoutlinecolor(true); if (this.outlineColor !== undefined) {
} else { playerDetails.setOutlinecolor(new UInt32Value().setValue(this.outlineColor));
playerDetails.setOutlinecolor(value); }
if (this.voiceIndicatorShown !== undefined) {
playerDetails.setShowvoiceindicator(new BoolValue().setValue(this.voiceIndicatorShown));
} }
this.positionNotifier.updatePlayerDetails(this, playerDetails); this.positionNotifier.updatePlayerDetails(this, playerDetails);

View File

@ -338,11 +338,12 @@ export class SocketManager {
userJoinedZoneMessage.setVisitcardurl(thing.visitCardUrl); userJoinedZoneMessage.setVisitcardurl(thing.visitCardUrl);
} }
userJoinedZoneMessage.setCompanion(thing.companion); userJoinedZoneMessage.setCompanion(thing.companion);
if (thing.outlineColor === undefined) { const outlineColor = thing.getOutlineColor();
if (outlineColor === undefined) {
userJoinedZoneMessage.setHasoutline(false); userJoinedZoneMessage.setHasoutline(false);
} else { } else {
userJoinedZoneMessage.setHasoutline(true); userJoinedZoneMessage.setHasoutline(true);
userJoinedZoneMessage.setOutlinecolor(thing.outlineColor); userJoinedZoneMessage.setOutlinecolor(outlineColor);
} }
const subMessage = new SubToPusherMessage(); const subMessage = new SubToPusherMessage();

View File

@ -77,12 +77,6 @@ export interface ItemEventMessageInterface {
parameters: unknown; parameters: unknown;
} }
export interface PlayerDetailsUpdatedMessageInterface {
userId: number;
outlineColor: number;
removeOutlineColor: boolean;
}
export interface RoomJoinedMessageInterface { export interface RoomJoinedMessageInterface {
//users: MessageUserPositionInterface[], //users: MessageUserPositionInterface[],
//groups: GroupCreatedUpdatedMessageInterface[], //groups: GroupCreatedUpdatedMessageInterface[],

View File

@ -504,6 +504,20 @@ export class RoomConnection implements RoomConnection {
this.socket.send(clientToServerMessage.serializeBinary().buffer); this.socket.send(clientToServerMessage.serializeBinary().buffer);
}*/ }*/
public emitPlayerShowVoiceIndicator(show: boolean): void {
const message = SetPlayerDetailsMessageTsProto.fromPartial({
showVoiceIndicator: show,
});
const bytes = ClientToServerMessageTsProto.encode({
message: {
$case: "setPlayerDetailsMessage",
setPlayerDetailsMessage: message,
},
}).finish();
this.socket.send(bytes);
}
public emitPlayerOutlineColor(color: number | null) { public emitPlayerOutlineColor(color: number | null) {
let message: SetPlayerDetailsMessageTsProto; let message: SetPlayerDetailsMessageTsProto;
if (color === null) { if (color === null) {

View File

@ -54,7 +54,6 @@ import type {
MessageUserMovedInterface, MessageUserMovedInterface,
MessageUserPositionInterface, MessageUserPositionInterface,
OnConnectInterface, OnConnectInterface,
PlayerDetailsUpdatedMessageInterface,
PointInterface, PointInterface,
PositionInterface, PositionInterface,
RoomJoinedMessageInterface, RoomJoinedMessageInterface,
@ -102,6 +101,7 @@ import type { VideoPeer } from "../../WebRtc/VideoPeer";
import CancelablePromise from "cancelable-promise"; import CancelablePromise from "cancelable-promise";
import { Deferred } from "ts-deferred"; import { Deferred } from "ts-deferred";
import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin"; import { SuperLoaderPlugin } from "../Services/SuperLoaderPlugin";
import { PlayerDetailsUpdatedMessage } from "../../Messages/ts-proto-generated/protos/messages";
export interface GameSceneInitInterface { export interface GameSceneInitInterface {
initPosition: PointInterface | null; initPosition: PointInterface | null;
reconnecting: boolean; reconnecting: boolean;
@ -139,7 +139,7 @@ interface DeleteGroupEventInterface {
interface PlayerDetailsUpdatedInterface { interface PlayerDetailsUpdatedInterface {
type: "PlayerDetailsUpdated"; type: "PlayerDetailsUpdated";
details: PlayerDetailsUpdatedMessageInterface; details: PlayerDetailsUpdatedMessage;
} }
export class GameScene extends DirtyScene { export class GameScene extends DirtyScene {
@ -175,7 +175,6 @@ export class GameScene extends DirtyScene {
private emoteUnsubscribe!: Unsubscriber; private emoteUnsubscribe!: Unsubscriber;
private emoteMenuUnsubscribe!: Unsubscriber; private emoteMenuUnsubscribe!: Unsubscriber;
private volumeStoreUnsubscribers: Map<number, Unsubscriber> = new Map<number, Unsubscriber>();
private localVolumeStoreUnsubscriber: Unsubscriber | undefined; private localVolumeStoreUnsubscriber: Unsubscriber | undefined;
private followUsersColorStoreUnsubscribe!: Unsubscriber; private followUsersColorStoreUnsubscribe!: Unsubscriber;
private currentPlayerGroupIdStoreUnsubscribe!: Unsubscriber; private currentPlayerGroupIdStoreUnsubscribe!: Unsubscriber;
@ -220,6 +219,7 @@ export class GameScene extends DirtyScene {
private loader: Loader; private loader: Loader;
private lastCameraEvent: WasCameraUpdatedEvent | undefined; private lastCameraEvent: WasCameraUpdatedEvent | undefined;
private firstCameraUpdateSent: boolean = false; private firstCameraUpdateSent: boolean = false;
private showVoiceIndicatorChangeMessageSent: boolean = false;
private currentPlayerGroupId?: number; private currentPlayerGroupId?: number;
public readonly superLoad: SuperLoaderPlugin; public readonly superLoad: SuperLoaderPlugin;
@ -634,55 +634,43 @@ export class GameScene extends DirtyScene {
} }
const talkIconVolumeTreshold = 10; const talkIconVolumeTreshold = 10;
const oldPeers = new Map<number, VideoPeer>(); let oldPeersNumber = 0;
this.peerStoreUnsubscribe = peerStore.subscribe((peers) => { this.peerStoreUnsubscribe = peerStore.subscribe((peers) => {
this.volumeStoreUnsubscribers.forEach((unsubscribe) => unsubscribe());
this.volumeStoreUnsubscribers.clear();
for (const [key, videoStream] of peers) {
this.volumeStoreUnsubscribers.set(
key,
videoStream.volumeStore.subscribe((volume) => {
if (volume) {
this.MapPlayersByKey.get(key)?.showTalkIcon(volume > talkIconVolumeTreshold);
}
})
);
}
const newPeerNumber = peers.size; const newPeerNumber = peers.size;
if (newPeerNumber > oldPeers.size) { if (newPeerNumber > oldPeersNumber) {
this.playSound("audio-webrtc-in"); this.playSound("audio-webrtc-in");
} else if (newPeerNumber < oldPeers.size) { } else if (newPeerNumber < oldPeersNumber) {
this.playSound("audio-webrtc-out"); this.playSound("audio-webrtc-out");
const oldPeersKeys = oldPeers.keys();
const newPeersKeys = Array.from(peers.keys());
for (const oldKey of oldPeersKeys) {
if (!newPeersKeys.includes(oldKey)) {
this.MapPlayersByKey.get(oldKey)?.showTalkIcon(false, true);
}
}
} }
if (newPeerNumber > 0) { if (newPeerNumber > 0) {
if (!this.localVolumeStoreUnsubscriber) { if (!this.localVolumeStoreUnsubscriber) {
this.localVolumeStoreUnsubscriber = localVolumeStore.subscribe((volume) => { this.localVolumeStoreUnsubscriber = localVolumeStore.subscribe((volume) => {
if (volume) { if (volume === undefined) {
this.CurrentPlayer.showTalkIcon(volume > talkIconVolumeTreshold); return;
}
const aboveTreshold = volume > talkIconVolumeTreshold;
this.CurrentPlayer.showTalkIcon(aboveTreshold);
if (this.showVoiceIndicatorChangeMessageSent && !aboveTreshold) {
this.connection?.emitPlayerShowVoiceIndicator(false);
this.showVoiceIndicatorChangeMessageSent = false;
} else if (!this.showVoiceIndicatorChangeMessageSent && aboveTreshold) {
this.connection?.emitPlayerShowVoiceIndicator(true);
this.showVoiceIndicatorChangeMessageSent = true;
} }
}); });
} }
} else { } else {
this.CurrentPlayer.showTalkIcon(false, true); this.CurrentPlayer.showTalkIcon(false, true);
this.connection?.emitPlayerShowVoiceIndicator(false);
this.showVoiceIndicatorChangeMessageSent = false;
this.MapPlayersByKey.forEach((remotePlayer) => remotePlayer.showTalkIcon(false, true)); this.MapPlayersByKey.forEach((remotePlayer) => remotePlayer.showTalkIcon(false, true));
if (this.localVolumeStoreUnsubscriber) { if (this.localVolumeStoreUnsubscriber) {
this.localVolumeStoreUnsubscriber(); this.localVolumeStoreUnsubscriber();
this.localVolumeStoreUnsubscriber = undefined; this.localVolumeStoreUnsubscriber = undefined;
} }
} }
oldPeers.clear(); oldPeersNumber = peers.size;
for (const [key, val] of peers) {
oldPeers.set(key, val);
}
}); });
this.emoteUnsubscribe = emoteStore.subscribe((emote) => { this.emoteUnsubscribe = emoteStore.subscribe((emote) => {
@ -834,11 +822,7 @@ export class GameScene extends DirtyScene {
} }
this.pendingEvents.enqueue({ this.pendingEvents.enqueue({
type: "PlayerDetailsUpdated", type: "PlayerDetailsUpdated",
details: { details: message,
userId: message.userId,
outlineColor: message.details.outlineColor,
removeOutlineColor: message.details.removeOutlineColor,
},
}); });
}); });
@ -2070,7 +2054,7 @@ ${escapedMessage}
this.groups.delete(groupId); this.groups.delete(groupId);
} }
doUpdatePlayerDetails(message: PlayerDetailsUpdatedMessageInterface): void { doUpdatePlayerDetails(message: PlayerDetailsUpdatedMessage): void {
const character = this.MapPlayersByKey.get(message.userId); const character = this.MapPlayersByKey.get(message.userId);
if (character === undefined) { if (character === undefined) {
console.log( console.log(
@ -2080,10 +2064,13 @@ ${escapedMessage}
); );
return; return;
} }
if (message.removeOutlineColor) { if (message.details?.removeOutlineColor) {
character.removeApiOutlineColor(); character.removeApiOutlineColor();
} else { } else if (message.details?.outlineColor !== undefined) {
character.setApiOutlineColor(message.outlineColor); character.setApiOutlineColor(message.details?.outlineColor);
}
if (message.details?.showVoiceIndicator !== undefined) {
character.showTalkIcon(message.details?.showVoiceIndicator);
} }
} }

View File

@ -50,12 +50,9 @@ message PingMessage {
} }
message SetPlayerDetailsMessage { message SetPlayerDetailsMessage {
//string name = 1; google.protobuf.UInt32Value outlineColor = 3;
//repeated string characterLayers = 2; google.protobuf.BoolValue removeOutlineColor = 4;
google.protobuf.BoolValue showVoiceIndicator = 5;
// TODO: switch to google.protobuf.Int32Value when we migrate to ts-proto
uint32 outlineColor = 3;
bool removeOutlineColor = 4;
} }
message UserMovesMessage { message UserMovesMessage {

View File

@ -87,7 +87,7 @@ export class UserDescriptor {
if (playerDetails.getRemoveoutlinecolor()) { if (playerDetails.getRemoveoutlinecolor()) {
this.outlineColor = undefined; this.outlineColor = undefined;
} else { } else {
this.outlineColor = playerDetails.getOutlinecolor(); this.outlineColor = playerDetails.getOutlinecolor()?.getValue();
} }
} }